1*4486d057Sjob /* $OpenBSD: print.c,v 1.27 2023/03/07 14:49:32 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 60cc6f004eSjob strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm); 61cc6f004eSjob 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)); 107*4486d057Sjob printf("Trust anchor locations: "); 108*4486d057Sjob for (i = 0; i < p->urisz; i++) { 109*4486d057Sjob if (i > 0) 110*4486d057Sjob printf("%26s", ""); 111*4486d057Sjob printf("%s\n", p->uri[i]); 112*4486d057Sjob } 113530399e8Sjob } 114e911df76Sjob 115e911df76Sjob EVP_PKEY_free(pk); 116e911df76Sjob free(rder); 117e911df76Sjob free(ski); 118714f4e3fSclaudio } 119714f4e3fSclaudio 120714f4e3fSclaudio void 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) { 147e0b87278Sjob printf("\t\"cert_issuer\": \"%s\",\n", issuer); 148530399e8Sjob printf("\t\"cert_serial\": \"%s\",\n", 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 159530399e8Sjob void 160714f4e3fSclaudio cert_print(const struct cert *p) 161714f4e3fSclaudio { 162530399e8Sjob size_t i, j; 163714f4e3fSclaudio char buf1[64], buf2[64]; 164714f4e3fSclaudio int sockt; 165530399e8Sjob 166530399e8Sjob if (outformats & FORMAT_JSON) { 167530399e8Sjob if (p->pubkey != NULL) 168530399e8Sjob printf("\t\"type\": \"router_key\",\n"); 169530399e8Sjob else 170530399e8Sjob printf("\t\"type\": \"ca_cert\",\n"); 171530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 172530399e8Sjob if (p->aki != NULL) 173530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 174530399e8Sjob x509_print(p->x509); 175530399e8Sjob if (p->aia != NULL) 176530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 177530399e8Sjob if (p->mft != NULL) 178530399e8Sjob printf("\t\"manifest\": \"%s\",\n", p->mft); 179530399e8Sjob if (p->repo != NULL) 180530399e8Sjob printf("\t\"carepository\": \"%s\",\n", p->repo); 181530399e8Sjob if (p->notify != NULL) 182530399e8Sjob printf("\t\"notify_url\": \"%s\",\n", p->notify); 183530399e8Sjob if (p->pubkey != NULL) 184530399e8Sjob printf("\t\"router_key\": \"%s\",\n", p->pubkey); 185530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 186530399e8Sjob printf("\t\"subordinate_resources\": [\n"); 187530399e8Sjob } else { 188714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 189714f4e3fSclaudio if (p->aki != NULL) 19067642cb5Stb printf("Authority key identifier: %s\n", 19167642cb5Stb pretty_key_id(p->aki)); 192530399e8Sjob x509_print(p->x509); 193714f4e3fSclaudio if (p->aia != NULL) 194714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 195714f4e3fSclaudio if (p->mft != NULL) 196714f4e3fSclaudio printf("Manifest: %s\n", p->mft); 197714f4e3fSclaudio if (p->repo != NULL) 198714f4e3fSclaudio printf("caRepository: %s\n", p->repo); 199714f4e3fSclaudio if (p->notify != NULL) 200714f4e3fSclaudio printf("Notify URL: %s\n", p->notify); 20114d83341Sjob if (p->pubkey != NULL) { 2026530cf17Sjob printf("BGPsec ECDSA public key: %s\n", 20367642cb5Stb p->pubkey); 20428439bbcSjob printf("Router key valid until: %s\n", 20528439bbcSjob time2str(p->expires)); 20614d83341Sjob } else 20728439bbcSjob printf("Certificate valid until: %s\n", 20828439bbcSjob time2str(p->expires)); 209*4486d057Sjob printf("Subordinate resources: "); 210530399e8Sjob } 211714f4e3fSclaudio 212530399e8Sjob for (i = 0; i < p->asz; i++) { 213714f4e3fSclaudio switch (p->as[i].type) { 214714f4e3fSclaudio case CERT_AS_ID: 215530399e8Sjob if (outformats & FORMAT_JSON) 216530399e8Sjob printf("\t\t{ \"asid\": %u }", p->as[i].id); 217*4486d057Sjob else { 218*4486d057Sjob if (i > 0) 219*4486d057Sjob printf("%26s", ""); 220*4486d057Sjob printf("AS: %u", p->as[i].id); 221*4486d057Sjob } 222714f4e3fSclaudio break; 223714f4e3fSclaudio case CERT_AS_INHERIT: 224530399e8Sjob if (outformats & FORMAT_JSON) 225530399e8Sjob printf("\t\t{ \"asid_inherit\": \"true\" }"); 226*4486d057Sjob else { 227*4486d057Sjob if (i > 0) 228*4486d057Sjob printf("%26s", ""); 229*4486d057Sjob printf("AS: inherit"); 230*4486d057Sjob } 231714f4e3fSclaudio break; 232714f4e3fSclaudio case CERT_AS_RANGE: 233530399e8Sjob if (outformats & FORMAT_JSON) 234530399e8Sjob printf("\t\t{ \"asrange\": { \"min\": %u, " 235530399e8Sjob "\"max\": %u }}", p->as[i].range.min, 236530399e8Sjob p->as[i].range.max); 237*4486d057Sjob else { 238*4486d057Sjob if (i > 0) 239*4486d057Sjob printf("%26s", ""); 240*4486d057Sjob printf("AS: %u -- %u", p->as[i].range.min, 241*4486d057Sjob p->as[i].range.max); 242*4486d057Sjob } 243714f4e3fSclaudio break; 244714f4e3fSclaudio } 245530399e8Sjob if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz) 246530399e8Sjob printf(",\n"); 247530399e8Sjob else 248530399e8Sjob printf("\n"); 249714f4e3fSclaudio } 250714f4e3fSclaudio 251530399e8Sjob for (j = 0; j < p->ipsz; j++) { 252530399e8Sjob switch (p->ips[j].type) { 253530399e8Sjob case CERT_IP_INHERIT: 254530399e8Sjob if (outformats & FORMAT_JSON) 255530399e8Sjob printf("\t\t{ \"ip_inherit\": \"true\" }"); 256*4486d057Sjob else { 257*4486d057Sjob if (i > 0 || j > 0) 258*4486d057Sjob printf("%26s", ""); 259*4486d057Sjob printf("IP: inherit"); 260*4486d057Sjob } 261530399e8Sjob break; 262530399e8Sjob case CERT_IP_ADDR: 263530399e8Sjob ip_addr_print(&p->ips[j].ip, 264530399e8Sjob p->ips[j].afi, buf1, sizeof(buf1)); 265530399e8Sjob if (outformats & FORMAT_JSON) 266530399e8Sjob printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1); 267*4486d057Sjob else { 268*4486d057Sjob if (i > 0 || j > 0) 269*4486d057Sjob printf("%26s", ""); 270*4486d057Sjob printf("IP: %s", buf1); 271*4486d057Sjob } 272530399e8Sjob break; 273530399e8Sjob case CERT_IP_RANGE: 274530399e8Sjob sockt = (p->ips[j].afi == AFI_IPV4) ? 275530399e8Sjob AF_INET : AF_INET6; 276530399e8Sjob inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1)); 277530399e8Sjob inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2)); 278530399e8Sjob if (outformats & FORMAT_JSON) 279530399e8Sjob printf("\t\t{ \"ip_range\": { \"min\": \"%s\"" 280530399e8Sjob ", \"max\": \"%s\" }}", buf1, buf2); 281*4486d057Sjob else { 282*4486d057Sjob if (i > 0 || j > 0) 283*4486d057Sjob printf("%26s", ""); 284*4486d057Sjob printf("IP: %s -- %s", buf1, buf2); 285*4486d057Sjob } 286530399e8Sjob break; 287530399e8Sjob } 288530399e8Sjob if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz) 289530399e8Sjob printf(",\n"); 290530399e8Sjob else 291530399e8Sjob printf("\n"); 292530399e8Sjob } 293530399e8Sjob 294530399e8Sjob if (outformats & FORMAT_JSON) 295530399e8Sjob printf("\t],\n"); 296714f4e3fSclaudio } 297714f4e3fSclaudio 298714f4e3fSclaudio void 299220c707cSclaudio crl_print(const struct crl *p) 300220c707cSclaudio { 301220c707cSclaudio STACK_OF(X509_REVOKED) *revlist; 302220c707cSclaudio X509_REVOKED *rev; 3037cdd491fSclaudio ASN1_INTEGER *crlnum; 304e0b87278Sjob X509_NAME *xissuer; 305220c707cSclaudio int i; 306e0b87278Sjob char *issuer, *serial; 307220c707cSclaudio time_t t; 308220c707cSclaudio 309530399e8Sjob if (outformats & FORMAT_JSON) { 310530399e8Sjob printf("\t\"type\": \"crl\",\n"); 311530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 312530399e8Sjob } else 313220c707cSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 3147cdd491fSclaudio 315e0b87278Sjob xissuer = X509_CRL_get_issuer(p->x509_crl); 316e0b87278Sjob issuer = X509_NAME_oneline(xissuer, NULL, 0); 3177cdd491fSclaudio crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL); 3187cdd491fSclaudio serial = x509_convert_seqnum(__func__, crlnum); 319e0b87278Sjob if (issuer != NULL && serial != NULL) { 320e0b87278Sjob if (outformats & FORMAT_JSON) { 321e0b87278Sjob printf("\t\"crl_issuer\": \"%s\",\n", issuer); 322530399e8Sjob printf("\t\"crl_serial\": \"%s\",\n", serial); 323e0b87278Sjob } else { 324e0b87278Sjob printf("CRL issuer: %s\n", issuer); 325e0b87278Sjob printf("CRL serial number: %s\n", serial); 326530399e8Sjob } 327e0b87278Sjob } 328e0b87278Sjob free(issuer); 3297cdd491fSclaudio free(serial); 3307cdd491fSclaudio ASN1_INTEGER_free(crlnum); 3317cdd491fSclaudio 332530399e8Sjob if (outformats & FORMAT_JSON) { 333530399e8Sjob printf("\t\"valid_since\": %lld,\n", (long long)p->issued); 334530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 335530399e8Sjob printf("\t\"revoked_certs\": [\n"); 336530399e8Sjob } else { 337220c707cSclaudio printf("CRL valid since: %s\n", time2str(p->issued)); 338220c707cSclaudio printf("CRL valid until: %s\n", time2str(p->expires)); 339530399e8Sjob printf("Revoked Certificates:\n"); 340530399e8Sjob } 341220c707cSclaudio 342220c707cSclaudio revlist = X509_CRL_get_REVOKED(p->x509_crl); 343220c707cSclaudio for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) { 344220c707cSclaudio rev = sk_X509_REVOKED_value(revlist, i); 3457cdd491fSclaudio serial = x509_convert_seqnum(__func__, 3467cdd491fSclaudio X509_REVOKED_get0_serialNumber(rev)); 347220c707cSclaudio x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t); 348530399e8Sjob if (serial != NULL) { 349530399e8Sjob if (outformats & FORMAT_JSON) { 350530399e8Sjob printf("\t\t{ \"serial\": \"%s\"", serial); 351530399e8Sjob printf(", \"date\": \"%s\" }", time2str(t)); 352530399e8Sjob if (i + 1 < sk_X509_REVOKED_num(revlist)) 353530399e8Sjob printf(","); 354530399e8Sjob printf("\n"); 355530399e8Sjob } else 356*4486d057Sjob printf("%25s Serial: %8s Revocation Date: %s" 357*4486d057Sjob "\n", "", serial, time2str(t)); 358530399e8Sjob } 3597cdd491fSclaudio free(serial); 360220c707cSclaudio } 361530399e8Sjob 362530399e8Sjob if (outformats & FORMAT_JSON) 363530399e8Sjob printf("\t],\n"); 364530399e8Sjob else if (i == 0) 365220c707cSclaudio printf("No Revoked Certificates\n"); 366220c707cSclaudio } 367220c707cSclaudio 368220c707cSclaudio void 369530399e8Sjob mft_print(const X509 *x, const struct mft *p) 370714f4e3fSclaudio { 371714f4e3fSclaudio size_t i; 372714f4e3fSclaudio char *hash; 373714f4e3fSclaudio 374530399e8Sjob if (outformats & FORMAT_JSON) { 375530399e8Sjob printf("\t\"type\": \"manifest\",\n"); 376530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 377530399e8Sjob x509_print(x); 378530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 379530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 3802cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 381530399e8Sjob printf("\t\"manifest_number\": \"%s\",\n", p->seqnum); 382530399e8Sjob printf("\t\"valid_since\": %lld,\n", (long long)p->valid_since); 383530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->valid_until); 384530399e8Sjob } else { 385714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 386714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 387530399e8Sjob x509_print(x); 388714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 3892cf0e122Sjob printf("Subject info access: %s\n", p->sia); 390714f4e3fSclaudio printf("Manifest Number: %s\n", p->seqnum); 391530399e8Sjob printf("Manifest valid since: %s\n", time2str(p->valid_since)); 392530399e8Sjob printf("Manifest valid until: %s\n", time2str(p->valid_until)); 393*4486d057Sjob printf("Files and hashes: "); 394530399e8Sjob } 395530399e8Sjob 396714f4e3fSclaudio for (i = 0; i < p->filesz; i++) { 397530399e8Sjob if (i == 0 && outformats & FORMAT_JSON) 398530399e8Sjob printf("\t\"filesandhashes\": [\n"); 399530399e8Sjob 400714f4e3fSclaudio if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 401714f4e3fSclaudio &hash) == -1) 402714f4e3fSclaudio errx(1, "base64_encode failure"); 403530399e8Sjob 404530399e8Sjob if (outformats & FORMAT_JSON) { 405530399e8Sjob printf("\t\t{ \"filename\": \"%s\",", p->files[i].file); 406530399e8Sjob printf(" \"hash\": \"%s\" }", hash); 407530399e8Sjob if (i + 1 < p->filesz) 408530399e8Sjob printf(","); 409530399e8Sjob printf("\n"); 410530399e8Sjob } else { 411*4486d057Sjob if (i > 0) 412*4486d057Sjob printf("%26s", ""); 413*4486d057Sjob printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file, 414*4486d057Sjob hash); 415530399e8Sjob } 416530399e8Sjob 417714f4e3fSclaudio free(hash); 418714f4e3fSclaudio } 419530399e8Sjob 420530399e8Sjob if (outformats & FORMAT_JSON) 421530399e8Sjob printf("\t],\n"); 422714f4e3fSclaudio } 423714f4e3fSclaudio 424714f4e3fSclaudio void 425530399e8Sjob roa_print(const X509 *x, const struct roa *p) 426714f4e3fSclaudio { 427714f4e3fSclaudio char buf[128]; 428714f4e3fSclaudio size_t i; 429714f4e3fSclaudio 430530399e8Sjob if (outformats & FORMAT_JSON) { 431530399e8Sjob printf("\t\"type\": \"roa\",\n"); 432530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 433530399e8Sjob x509_print(x); 434530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 435530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 4362cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 437530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 438530399e8Sjob } else { 439714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 440530399e8Sjob x509_print(x); 441714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 442714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 4432cf0e122Sjob printf("Subject info access: %s\n", p->sia); 444220c707cSclaudio printf("ROA valid until: %s\n", time2str(p->expires)); 445714f4e3fSclaudio printf("asID: %u\n", p->asid); 446*4486d057Sjob printf("IP address blocks: "); 447530399e8Sjob } 448530399e8Sjob 449714f4e3fSclaudio for (i = 0; i < p->ipsz; i++) { 450530399e8Sjob if (i == 0 && outformats & FORMAT_JSON) 451530399e8Sjob printf("\t\"vrps\": [\n"); 452530399e8Sjob 453714f4e3fSclaudio ip_addr_print(&p->ips[i].addr, 454714f4e3fSclaudio p->ips[i].afi, buf, sizeof(buf)); 455530399e8Sjob 456530399e8Sjob if (outformats & FORMAT_JSON) { 457530399e8Sjob printf("\t\t{ \"prefix\": \"%s\",", buf); 458530399e8Sjob printf(" \"asid\": %u,", p->asid); 459530399e8Sjob printf(" \"maxlen\": %hhu }", p->ips[i].maxlength); 460530399e8Sjob if (i + 1 < p->ipsz) 461530399e8Sjob printf(","); 462530399e8Sjob printf("\n"); 463*4486d057Sjob } else { 464*4486d057Sjob if (i > 0) 465*4486d057Sjob printf("%26s", ""); 466*4486d057Sjob printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength); 467*4486d057Sjob } 468714f4e3fSclaudio } 469530399e8Sjob 470530399e8Sjob if (outformats & FORMAT_JSON) 471530399e8Sjob printf("\t],\n"); 472714f4e3fSclaudio } 473714f4e3fSclaudio 474714f4e3fSclaudio void 475530399e8Sjob gbr_print(const X509 *x, const struct gbr *p) 476714f4e3fSclaudio { 477530399e8Sjob size_t i; 478530399e8Sjob 479530399e8Sjob if (outformats & FORMAT_JSON) { 480530399e8Sjob printf("\t\"type\": \"gbr\",\n"); 481530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 482530399e8Sjob x509_print(x); 483530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 484530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 4852cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 486530399e8Sjob printf("\t\"vcard\": \""); 487530399e8Sjob for (i = 0; i < strlen(p->vcard); i++) { 488530399e8Sjob if (p->vcard[i] == '"') 489530399e8Sjob printf("\\\""); 490530399e8Sjob if (p->vcard[i] == '\r') 491530399e8Sjob continue; 492530399e8Sjob if (p->vcard[i] == '\n') 493530399e8Sjob printf("\\r\\n"); 494530399e8Sjob else 495530399e8Sjob putchar(p->vcard[i]); 496530399e8Sjob } 497530399e8Sjob printf("\",\n"); 498530399e8Sjob } else { 499714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 500530399e8Sjob x509_print(x); 501714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 502714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 5032cf0e122Sjob printf("Subject info access: %s\n", p->sia); 504714f4e3fSclaudio printf("vcard:\n%s", p->vcard); 505714f4e3fSclaudio } 506530399e8Sjob } 50704834fbdSjob 50804834fbdSjob void 50904834fbdSjob rsc_print(const X509 *x, const struct rsc *p) 51004834fbdSjob { 51128439bbcSjob char buf1[64], buf2[64]; 51204834fbdSjob char *hash; 51304834fbdSjob int sockt; 51404834fbdSjob size_t i, j; 51504834fbdSjob 51604834fbdSjob if (outformats & FORMAT_JSON) { 51704834fbdSjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 51804834fbdSjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 51904834fbdSjob x509_print(x); 52004834fbdSjob printf("\t\"aia\": \"%s\",\n", p->aia); 52104834fbdSjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 52204834fbdSjob printf("\t\"signed_with_resources\": [\n"); 52304834fbdSjob } else { 52404834fbdSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 52504834fbdSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 52604834fbdSjob x509_print(x); 52704834fbdSjob printf("Authority info access: %s\n", p->aia); 52828439bbcSjob printf("RSC valid until: %s\n", time2str(p->expires)); 529*4486d057Sjob printf("Signed with resources: "); 53004834fbdSjob } 53104834fbdSjob 53204834fbdSjob for (i = 0; i < p->asz; i++) { 53304834fbdSjob switch (p->as[i].type) { 53404834fbdSjob case CERT_AS_ID: 53504834fbdSjob if (outformats & FORMAT_JSON) 53604834fbdSjob printf("\t\t{ \"asid\": %u }", p->as[i].id); 537*4486d057Sjob else { 538*4486d057Sjob if (i > 0) 539*4486d057Sjob printf("%26s", ""); 540*4486d057Sjob printf("AS: %u", p->as[i].id); 541*4486d057Sjob } 54204834fbdSjob break; 54304834fbdSjob case CERT_AS_RANGE: 54404834fbdSjob if (outformats & FORMAT_JSON) 54504834fbdSjob printf("\t\t{ \"asrange\": { \"min\": %u, " 54604834fbdSjob "\"max\": %u }}", p->as[i].range.min, 54704834fbdSjob p->as[i].range.max); 548*4486d057Sjob else { 549*4486d057Sjob if (i > 0) 550*4486d057Sjob printf("%26s", ""); 551*4486d057Sjob printf("AS: %u -- %u", p->as[i].range.min, 552*4486d057Sjob p->as[i].range.max); 553*4486d057Sjob } 55404834fbdSjob break; 55504834fbdSjob case CERT_AS_INHERIT: 55604834fbdSjob /* inheritance isn't possible in RSC */ 55704834fbdSjob break; 55804834fbdSjob } 55904834fbdSjob if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz) 56004834fbdSjob printf(",\n"); 56104834fbdSjob else 56204834fbdSjob printf("\n"); 56304834fbdSjob } 56404834fbdSjob 56504834fbdSjob for (j = 0; j < p->ipsz; j++) { 56604834fbdSjob switch (p->ips[j].type) { 56704834fbdSjob case CERT_IP_ADDR: 56804834fbdSjob ip_addr_print(&p->ips[j].ip, 56904834fbdSjob p->ips[j].afi, buf1, sizeof(buf1)); 57004834fbdSjob if (outformats & FORMAT_JSON) 57104834fbdSjob printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1); 572*4486d057Sjob else { 573*4486d057Sjob if (i > 0 || j > 0) 574*4486d057Sjob printf("%26s", ""); 575*4486d057Sjob printf("IP: %s", buf1); 576*4486d057Sjob } 57704834fbdSjob break; 57804834fbdSjob case CERT_IP_RANGE: 57904834fbdSjob sockt = (p->ips[j].afi == AFI_IPV4) ? 58004834fbdSjob AF_INET : AF_INET6; 58104834fbdSjob inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1)); 58204834fbdSjob inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2)); 58304834fbdSjob if (outformats & FORMAT_JSON) 58404834fbdSjob printf("\t\t{ \"ip_range\": { \"min\": \"%s\"" 58504834fbdSjob ", \"max\": \"%s\" }}", buf1, buf2); 586*4486d057Sjob else { 587*4486d057Sjob if (i > 0 || j > 0) 588*4486d057Sjob printf("%26s", ""); 589*4486d057Sjob printf("IP: %s -- %s", buf1, buf2); 590*4486d057Sjob } 59104834fbdSjob break; 59204834fbdSjob case CERT_IP_INHERIT: 59304834fbdSjob /* inheritance isn't possible in RSC */ 59404834fbdSjob break; 59504834fbdSjob } 59604834fbdSjob if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz) 59704834fbdSjob printf(",\n"); 59804834fbdSjob else 59904834fbdSjob printf("\n"); 60004834fbdSjob } 60104834fbdSjob 60204834fbdSjob if (outformats & FORMAT_JSON) { 60304834fbdSjob printf("\t],\n"); 60404834fbdSjob printf("\t\"filenamesandhashes\": [\n"); 60504834fbdSjob } else 606*4486d057Sjob printf("Filenames and hashes: "); 60704834fbdSjob 60804834fbdSjob for (i = 0; i < p->filesz; i++) { 60904834fbdSjob if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 61004834fbdSjob &hash) == -1) 61104834fbdSjob errx(1, "base64_encode failure"); 61204834fbdSjob 61304834fbdSjob if (outformats & FORMAT_JSON) { 61404834fbdSjob printf("\t\t{ \"filename\": \"%s\",", 61504834fbdSjob p->files[i].filename ? p->files[i].filename : ""); 61604834fbdSjob printf(" \"hash_digest\": \"%s\" }", hash); 61704834fbdSjob if (i + 1 < p->filesz) 61804834fbdSjob printf(","); 61904834fbdSjob printf("\n"); 62004834fbdSjob } else { 621*4486d057Sjob if (i > 0) 622*4486d057Sjob printf("%26s", ""); 623*4486d057Sjob printf("%zu: %s (hash: %s)\n", i + 1, 624*4486d057Sjob p->files[i].filename ? p->files[i].filename 625*4486d057Sjob : "no filename", hash); 62604834fbdSjob } 62704834fbdSjob 62804834fbdSjob free(hash); 62904834fbdSjob } 63004834fbdSjob 63104834fbdSjob if (outformats & FORMAT_JSON) 63204834fbdSjob printf("\t],\n"); 63304834fbdSjob } 634a29ddfd5Sjob 635a29ddfd5Sjob void 636a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p) 637a29ddfd5Sjob { 638a29ddfd5Sjob size_t i; 639a29ddfd5Sjob 640a29ddfd5Sjob if (outformats & FORMAT_JSON) { 641a29ddfd5Sjob printf("\t\"type\": \"aspa\",\n"); 642a29ddfd5Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 643a29ddfd5Sjob x509_print(x); 644a29ddfd5Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 645a29ddfd5Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 6462cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 64728439bbcSjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 648a29ddfd5Sjob printf("\t\"customer_asid\": %u,\n", p->custasid); 649a29ddfd5Sjob printf("\t\"provider_set\": [\n"); 650a29ddfd5Sjob for (i = 0; i < p->providersz; i++) { 651a29ddfd5Sjob printf("\t\t{ \"asid\": %u", p->providers[i].as); 652a29ddfd5Sjob if (p->providers[i].afi == AFI_IPV4) 653a29ddfd5Sjob printf(", \"afi_limit\": \"ipv4\""); 654a29ddfd5Sjob if (p->providers[i].afi == AFI_IPV6) 655a29ddfd5Sjob printf(", \"afi_limit\": \"ipv6\""); 656a29ddfd5Sjob printf(" }"); 657a29ddfd5Sjob if (i + 1 < p->providersz) 658a29ddfd5Sjob printf(","); 659a29ddfd5Sjob printf("\n"); 660a29ddfd5Sjob } 661a29ddfd5Sjob printf("\t],\n"); 662a29ddfd5Sjob } else { 663a29ddfd5Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 664a29ddfd5Sjob x509_print(x); 665a29ddfd5Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 666a29ddfd5Sjob printf("Authority info access: %s\n", p->aia); 6672cf0e122Sjob printf("Subject info access: %s\n", p->sia); 66828439bbcSjob printf("ASPA valid until: %s\n", time2str(p->expires)); 669a29ddfd5Sjob printf("Customer AS: %u\n", p->custasid); 670*4486d057Sjob printf("Provider Set: "); 671a29ddfd5Sjob for (i = 0; i < p->providersz; i++) { 672*4486d057Sjob if (i > 0) 673*4486d057Sjob printf("%26s", ""); 674*4486d057Sjob printf("AS: %d", p->providers[i].as); 675a29ddfd5Sjob switch (p->providers[i].afi) { 676a29ddfd5Sjob case AFI_IPV4: 677a29ddfd5Sjob printf(" (IPv4 only)"); 678a29ddfd5Sjob break; 679a29ddfd5Sjob case AFI_IPV6: 680a29ddfd5Sjob printf(" (IPv6 only)"); 681a29ddfd5Sjob break; 682a29ddfd5Sjob default: 683a29ddfd5Sjob break; 684a29ddfd5Sjob } 685a29ddfd5Sjob printf("\n"); 686a29ddfd5Sjob } 687a29ddfd5Sjob } 688a29ddfd5Sjob } 689ee2a33daSjob 690ee2a33daSjob static void 691ee2a33daSjob takey_print(char *name, const struct takey *t) 692ee2a33daSjob { 693ee2a33daSjob char *spki = NULL; 694ee2a33daSjob size_t i, j = 0; 695ee2a33daSjob 696ee2a33daSjob if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0) 697ee2a33daSjob errx(1, "base64_encode failed in %s", __func__); 698ee2a33daSjob 699ee2a33daSjob if (outformats & FORMAT_JSON) { 700ee2a33daSjob printf("\t\t{\n\t\t\t\"name\": \"%s\",\n", name); 701ee2a33daSjob printf("\t\t\t\"comments\": ["); 702ee2a33daSjob for (i = 0; i < t->commentsz; i++) { 703ee2a33daSjob printf("\"%s\"", t->comments[i]); 704ee2a33daSjob if (i + 1 < t->commentsz) 705ee2a33daSjob printf(", "); 706ee2a33daSjob } 707ee2a33daSjob printf("],\n"); 708ee2a33daSjob printf("\t\t\t\"uris\": ["); 709ee2a33daSjob for (i = 0; i < t->urisz; i++) { 710ee2a33daSjob printf("\"%s\"", t->uris[i]); 711ee2a33daSjob if (i + 1 < t->urisz) 712ee2a33daSjob printf(", "); 713ee2a33daSjob } 714ee2a33daSjob printf("],\n"); 715ee2a33daSjob printf("\t\t\t\"spki\": \"%s\"\n\t\t}", spki); 716ee2a33daSjob } else { 717ee2a33daSjob printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name); 718ee2a33daSjob 719ee2a33daSjob for (i = 0; i < t->commentsz; i++) { 720ee2a33daSjob printf("\t# %s\n", t->comments[i]); 721ee2a33daSjob } 722ee2a33daSjob 723ee2a33daSjob for (i = 0; i < t->urisz; i++) { 724ee2a33daSjob printf("\t%s\n\n\t", t->uris[i]); 725ee2a33daSjob } 726ee2a33daSjob 727ee2a33daSjob for (i = 0; i < strlen(spki); i++) { 728ee2a33daSjob printf("%c", spki[i]); 729ee2a33daSjob j++; 730ee2a33daSjob if (j == 64) { 731ee2a33daSjob printf("\n\t"); 732ee2a33daSjob j = 0; 733ee2a33daSjob } 734ee2a33daSjob } 735ee2a33daSjob 736ee2a33daSjob printf("\n\n"); 737ee2a33daSjob } 738ee2a33daSjob 739ee2a33daSjob free(spki); 740ee2a33daSjob } 741ee2a33daSjob 742ee2a33daSjob void 743ee2a33daSjob tak_print(const X509 *x, const struct tak *p) 744ee2a33daSjob { 745ee2a33daSjob if (outformats & FORMAT_JSON) { 746ee2a33daSjob printf("\t\"type\": \"tak\",\n"); 747ee2a33daSjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 748ee2a33daSjob x509_print(x); 749ee2a33daSjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 750ee2a33daSjob printf("\t\"aia\": \"%s\",\n", p->aia); 7512cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 752ee2a33daSjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 753ee2a33daSjob printf("\t\"takeys\": [\n"); 754ee2a33daSjob } else { 755ee2a33daSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 756ee2a33daSjob x509_print(x); 757ee2a33daSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 758ee2a33daSjob printf("Authority info access: %s\n", p->aia); 7592cf0e122Sjob printf("Subject info access: %s\n", p->sia); 76028439bbcSjob printf("TAK valid until: %s\n", time2str(p->expires)); 761ee2a33daSjob } 762ee2a33daSjob 763ee2a33daSjob takey_print("current", p->current); 764ee2a33daSjob 765ee2a33daSjob if (p->predecessor != NULL) { 766ee2a33daSjob if (outformats & FORMAT_JSON) 767ee2a33daSjob printf(",\n"); 768ee2a33daSjob takey_print("predecessor", p->predecessor); 769ee2a33daSjob } 770ee2a33daSjob 771ee2a33daSjob if (p->successor != NULL) { 772ee2a33daSjob if (outformats & FORMAT_JSON) 773ee2a33daSjob printf(",\n"); 774ee2a33daSjob takey_print("successor", p->successor); 775ee2a33daSjob } 776ee2a33daSjob 777ee2a33daSjob if (outformats & FORMAT_JSON) 778ee2a33daSjob printf("\n\t],\n"); 779ee2a33daSjob } 780ef3f6f56Sjob 781ef3f6f56Sjob void 782ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p) 783ef3f6f56Sjob { 784ef3f6f56Sjob char buf[128]; 785ef3f6f56Sjob size_t i; 786ef3f6f56Sjob 787ef3f6f56Sjob if (outformats & FORMAT_JSON) { 788ef3f6f56Sjob printf("\t\"type\": \"geofeed\",\n"); 789ef3f6f56Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 790ef3f6f56Sjob x509_print(x); 791ef3f6f56Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 792ef3f6f56Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 793ef3f6f56Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 794ef3f6f56Sjob printf("\t\"records\": [\n"); 795ef3f6f56Sjob } else { 796ef3f6f56Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 797ef3f6f56Sjob x509_print(x); 798ef3f6f56Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 799ef3f6f56Sjob printf("Authority info access: %s\n", p->aia); 800ef3f6f56Sjob printf("Geofeed valid until: %s\n", time2str(p->expires)); 801ef3f6f56Sjob printf("Geofeed CSV records:\n"); 802ef3f6f56Sjob } 803ef3f6f56Sjob 804ef3f6f56Sjob for (i = 0; i < p->geoipsz; i++) { 805ef3f6f56Sjob if (p->geoips[i].ip->type != CERT_IP_ADDR) 806ef3f6f56Sjob continue; 807ef3f6f56Sjob 808ef3f6f56Sjob ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf, 809ef3f6f56Sjob sizeof(buf)); 810ef3f6f56Sjob if (outformats & FORMAT_JSON) 811ef3f6f56Sjob printf("\t\t{ \"prefix\": \"%s\", \"location\": \"%s\"" 812ef3f6f56Sjob "}", buf, p->geoips[i].loc); 813*4486d057Sjob else { 814*4486d057Sjob if (i > 0) 815*4486d057Sjob printf("%26s", ""); 816*4486d057Sjob printf("IP: %s (%s)", buf, p->geoips[i].loc); 817*4486d057Sjob } 818ef3f6f56Sjob 819ef3f6f56Sjob if (outformats & FORMAT_JSON && i + 1 < p->geoipsz) 820ef3f6f56Sjob printf(",\n"); 821ef3f6f56Sjob else 822ef3f6f56Sjob printf("\n"); 823ef3f6f56Sjob } 824ef3f6f56Sjob 825ef3f6f56Sjob if (outformats & FORMAT_JSON) 826ef3f6f56Sjob printf("\t],\n"); 827ef3f6f56Sjob } 828