Add Concatenated JSON output in filemode (rpki-client -j -f *)
authorjob <job@openbsd.org>
Wed, 20 Apr 2022 10:46:20 +0000 (10:46 +0000)
committerjob <job@openbsd.org>
Wed, 20 Apr 2022 10:46:20 +0000 (10:46 +0000)
The schema is still work in progress.

OK claudio@

usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/mft.c
usr.sbin/rpki-client/parser.c
usr.sbin/rpki-client/print.c
usr.sbin/rpki-client/rpki-client.8

index 129e9b9..fea4bdf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: extern.h,v 1.128 2022/04/19 13:52:24 claudio Exp $ */
+/*     $OpenBSD: extern.h,v 1.129 2022/04/20 10:46:20 job Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -196,7 +196,7 @@ struct mft {
        char            *ski; /* SKI */
        char            *crl; /* CRL file name */
        unsigned char    crlhash[SHA256_DIGEST_LENGTH];
-       time_t           valid_from;
+       time_t           valid_since;
        time_t           valid_until;
        size_t           filesz; /* number of filenames */
        unsigned int     repoid;
@@ -598,12 +598,13 @@ int                x509_location(const char *, const char *, const char *,
 
 /* printers */
 char           *time2str(time_t);
+void            x509_print(const X509 *);
 void            tal_print(const struct tal *);
 void            cert_print(const struct cert *);
 void            crl_print(const struct crl *);
-void            mft_print(const struct mft *);
-void            roa_print(const struct roa *);
-void            gbr_print(const struct gbr *);
+void            mft_print(const X509 *, const struct mft *);
+void            roa_print(const X509 *, const struct roa *);
+void            gbr_print(const X509 *, const struct gbr *);
 
 /* Output! */
 
index 4d8caf2..565a441 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mft.c,v 1.59 2022/04/19 18:52:36 tb Exp $ */
+/*     $OpenBSD: mft.c,v 1.60 2022/04/20 10:46:20 job Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -86,7 +86,7 @@ mft_parse_time(const ASN1_GENERALIZEDTIME *from,
                return 0;
        }
 
-       if ((p->res->valid_from = timegm(&tm_from)) == -1 ||
+       if ((p->res->valid_since = timegm(&tm_from)) == -1 ||
            (p->res->valid_until = timegm(&tm_until)) == -1)
                errx(1, "%s: timegm failed", p->fn);
 
index abaa54c..7751bc1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parser.c,v 1.69 2022/04/19 13:25:08 claudio Exp $ */
+/*     $OpenBSD: parser.c,v 1.70 2022/04/20 10:46:20 job Exp $ */
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -428,9 +428,9 @@ proc_parser_mft_post(char *file, struct mft *mft, const char *path)
        }
 
        /* check that now is not before from */
-       if (now < mft->valid_from) {
+       if (now < mft->valid_since) {
                warnx("%s: mft not yet valid %s", file,
-                   time2str(mft->valid_from));
+                   time2str(mft->valid_since));
                mft->stale = 1;
        }
        /* check that now is not after until */
@@ -1038,8 +1038,12 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
        enum rtype type;
        int is_ta = 0;
 
-       if (num++ > 0)
-               printf("--\n");
+       if (num++ > 0) {
+               if (outformats & FORMAT_JSON)
+                       printf("\n");
+               else
+                       printf("--\n");
+       }
 
        if (strncmp(file, "rsync://", strlen("rsync://")) == 0) {
                file += strlen("rsync://");
@@ -1050,7 +1054,10 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                }
        }
 
-       printf("File: %s\n", file);
+       if (outformats & FORMAT_JSON)
+               printf("{\n\t\"file\": \"%s\",\n", file);
+       else
+               printf("File: %s\n", file);
 
        type = rtype_from_file_extension(file);
 
@@ -1081,7 +1088,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                mft = mft_parse(&x509, file, buf, len);
                if (mft == NULL)
                        break;
-               mft_print(mft);
+               mft_print(x509, mft);
                aia = mft->aia;
                aki = mft->aki;
                break;
@@ -1089,7 +1096,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                roa = roa_parse(&x509, file, buf, len);
                if (roa == NULL)
                        break;
-               roa_print(roa);
+               roa_print(x509, roa);
                aia = roa->aia;
                aki = roa->aki;
                break;
@@ -1097,7 +1104,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                gbr = gbr_parse(&x509, file, buf, len);
                if (gbr == NULL)
                        break;
-               gbr_print(gbr);
+               gbr_print(x509, gbr);
                aia = gbr->aia;
                aki = gbr->aki;
                break;
@@ -1112,6 +1119,11 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                break;
        }
 
+       if (outformats & FORMAT_JSON)
+               printf("\t\"validation\": \"");
+       else
+               printf("Validation: ");
+
        if (aia != NULL) {
                struct auth *a;
                struct crl *c;
@@ -1126,25 +1138,34 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                c = get_crl(a);
 
                if (valid_x509(file, x509, a, c, 0))
-                       printf("Validation: OK\n");
+                       printf("OK");
                else
-                       printf("Validation: Failed\n");
+                       printf("Failed");
        } else if (is_ta) {
                if ((tal = find_tal(cert)) != NULL) {
                        cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
-                       printf("TAL: %s\n", tal->descr);
-                       tal = NULL;
+                       if (cert != NULL)
+                               printf("OK");
                } else {
                        cert_free(cert);
                        cert = NULL;
-                       printf("TAL: not found\n");
+                       printf("Failed");
                }
-               if (cert != NULL)
-                       printf("Validation: OK\n");
-               else
-                       printf("Validation: Failed\n");
        }
 
+       if (is_ta) {
+               if (outformats & FORMAT_JSON) {
+                       printf("\",\n\t\"tal\": \"%s\"\n", tal->descr);
+                       printf("}");
+               } else {
+                       printf("\nTAL: %s\n", tal->descr);
+               }
+               tal = NULL;
+       } else if (outformats & FORMAT_JSON)
+                       printf("\"\n}");
+       else
+               printf("\n");
+
        X509_free(x509);
        cert_free(cert);
        crl_free(crl);
index d1a54f4..89bb4bb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: print.c,v 1.7 2022/04/12 11:05:50 job Exp $ */
+/*     $OpenBSD: print.c,v 1.8 2022/04/20 10:46:20 job Exp $ */
 /*
  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -73,7 +73,6 @@ tal_print(const struct tal *p)
        int                      rder_len;
        size_t                   i;
 
-       printf("Trust anchor name: %s\n", p->descr);
 
        der = p->pkey;
        pk = d2i_PUBKEY(NULL, &der, p->pkeysz);
@@ -90,76 +89,177 @@ tal_print(const struct tal *p)
                errx(1, "EVP_Digest failed in %s", __func__);
 
        ski = hex_encode(md, SHA_DIGEST_LENGTH);
-       printf("Subject key identifier: %s\n", pretty_key_id(ski));
-
-       printf("Trust anchor locations:\n");
-       for (i = 0; i < p->urisz; i++)
-               printf("%5zu: %s\n", i + 1, p->uri[i]);
 
+       if (outformats & FORMAT_JSON) {
+               printf("\t\"type\": \"tal\",\n");
+               printf("\t\"name\": %s\n", p->descr);
+               printf("\t\"ski\": %s\n", pretty_key_id(ski));
+               printf("\t\"trust_anchor_locations\": [");
+               for (i = 0; i < p->urisz; i++) {
+                       printf("\"%s\"", p->uri[i]);
+                       if (i + 1 < p->urisz)
+                               printf(", ");
+               }
+               printf("]\n");
+       
+       } else {
+               printf("Trust anchor name: %s\n", p->descr);
+               printf("Subject key identifier: %s\n", pretty_key_id(ski));
+               printf("Trust anchor locations:\n");
+               for (i = 0; i < p->urisz; i++)
+                       printf("%5zu: %s\n", i + 1, p->uri[i]);
+       }
+       
        EVP_PKEY_free(pk);
        free(rder);
        free(ski);
 }
 
+void
+x509_print(const X509 *x)
+{
+       const ASN1_INTEGER      *xserial;
+       char                    *serial = NULL;
+
+       xserial = X509_get0_serialNumber(x);
+       if (xserial == NULL) {
+               warnx("X509_get0_serialNumber failed in %s", __func__);
+               goto out;
+       }
+
+       serial = x509_convert_seqnum(__func__, xserial);
+       if (serial == NULL) {
+               warnx("x509_convert_seqnum failed in %s", __func__);
+               goto out;
+       }       
+
+       if (outformats & FORMAT_JSON) { 
+               printf("\t\"cert_serial\": \"%s\",\n", serial);
+       } else {
+               printf("Certificate serial: %s\n", serial);
+       }
+
+ out:
+       free(serial);
+}
+
 void
 cert_print(const struct cert *p)
 {
-       size_t   i;
-       char     buf1[64], buf2[64];
-       int      sockt;
-       char     tbuf[21];
+       size_t                   i, j;
+       char                     buf1[64], buf2[64];
+       int                      sockt;
+       char                     tbuf[21];
 
-       printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
-       if (p->aki != NULL)
-               printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
-       if (p->aia != NULL)
-               printf("Authority info access: %s\n", p->aia);
-       if (p->mft != NULL)
-               printf("Manifest: %s\n", p->mft);
-       if (p->repo != NULL)
-               printf("caRepository: %s\n", p->repo);
-       if (p->notify != NULL)
-               printf("Notify URL: %s\n", p->notify);
-       if (p->pubkey != NULL)
-               printf("BGPsec P-256 ECDSA public key: %s\n", p->pubkey);
        strftime(tbuf, sizeof(tbuf), "%FT%TZ", gmtime(&p->expires));
-       printf("Valid until: %s\n", tbuf);
 
-       printf("Subordinate Resources:\n");
+       if (outformats & FORMAT_JSON) {
+               if (p->pubkey != NULL)
+                       printf("\t\"type\": \"router_key\",\n");
+               else
+                       printf("\t\"type\": \"ca_cert\",\n");
+               printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
+               if (p->aki != NULL)
+                       printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
+               x509_print(p->x509);
+               if (p->aia != NULL)
+                       printf("\t\"aia\": \"%s\",\n", p->aia);
+               if (p->mft != NULL)
+                       printf("\t\"manifest\": \"%s\",\n", p->mft);
+               if (p->repo != NULL)
+                       printf("\t\"carepository\": \"%s\",\n", p->repo);
+               if (p->notify != NULL)
+                       printf("\t\"notify_url\": \"%s\",\n", p->notify);
+               if (p->pubkey != NULL)
+                       printf("\t\"router_key\": \"%s\",\n", p->pubkey);
+               printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
+               printf("\t\"subordinate_resources\": [\n");
+       } else {
+               printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
+               if (p->aki != NULL)
+                       printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
+               x509_print(p->x509);
+               if (p->aia != NULL)
+                       printf("Authority info access: %s\n", p->aia);
+               if (p->mft != NULL)
+                       printf("Manifest: %s\n", p->mft);
+               if (p->repo != NULL)
+                       printf("caRepository: %s\n", p->repo);
+               if (p->notify != NULL)
+                       printf("Notify URL: %s\n", p->notify);
+               if (p->pubkey != NULL)
+                       printf("BGPsec P-256 ECDSA public key: %s\n", p->pubkey);
+               printf("Valid until: %s\n", tbuf);
+               printf("Subordinate Resources:\n");
+       }
 
-       for (i = 0; i < p->asz; i++)
+       for (i = 0; i < p->asz; i++) {
                switch (p->as[i].type) {
                case CERT_AS_ID:
-                       printf("%5zu: AS: %u\n", i + 1, p->as[i].id);
+                       if (outformats & FORMAT_JSON)
+                               printf("\t\t{ \"asid\": %u }", p->as[i].id);
+                       else
+                               printf("%5zu: AS: %u", i + 1, p->as[i].id);
                        break;
                case CERT_AS_INHERIT:
-                       printf("%5zu: AS: inherit\n", i + 1);
+                       if (outformats & FORMAT_JSON)
+                               printf("\t\t{ \"asid_inherit\": \"true\" }");
+                       else
+                               printf("%5zu: AS: inherit", i + 1);
                        break;
                case CERT_AS_RANGE:
-                       printf("%5zu: AS: %u -- %u\n", i + 1,
-                               p->as[i].range.min, p->as[i].range.max);
+                       if (outformats & FORMAT_JSON)
+                               printf("\t\t{ \"asrange\": { \"min\": %u, "
+                                   "\"max\": %u }}", p->as[i].range.min,
+                                   p->as[i].range.max);
+                       else
+                               printf("%5zu: AS: %u -- %u", i + 1,
+                                   p->as[i].range.min, p->as[i].range.max);
                        break;
                }
+               if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz)
+                       printf(",\n");
+               else
+                       printf("\n");
+       }
 
-       for (i = 0; i < p->ipsz; i++)
-               switch (p->ips[i].type) {
+       for (j = 0; j < p->ipsz; j++) {
+               switch (p->ips[j].type) {
                case CERT_IP_INHERIT:
-                       printf("%5zu: IP: inherit\n", i + 1);
+                       if (outformats & FORMAT_JSON)
+                               printf("\t\t{ \"ip_inherit\": \"true\" }");
+                       else
+                               printf("%5zu: IP: inherit", i + j + 1);
                        break;
                case CERT_IP_ADDR:
-                       ip_addr_print(&p->ips[i].ip,
-                               p->ips[i].afi, buf1, sizeof(buf1));
-                       printf("%5zu: IP: %s\n", i + 1, buf1);
+                       ip_addr_print(&p->ips[j].ip,
+                           p->ips[j].afi, buf1, sizeof(buf1));
+                       if (outformats & FORMAT_JSON)
+                               printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1);
+                       else
+                               printf("%5zu: IP: %s", i + j + 1, buf1);
                        break;
                case CERT_IP_RANGE:
-                       sockt = (p->ips[i].afi == AFI_IPV4) ?
+                       sockt = (p->ips[j].afi == AFI_IPV4) ?
                                AF_INET : AF_INET6;
-                       inet_ntop(sockt, p->ips[i].min, buf1, sizeof(buf1));
-                       inet_ntop(sockt, p->ips[i].max, buf2, sizeof(buf2));
-                       printf("%5zu: IP: %s -- %s\n", i + 1, buf1, buf2);
+                       inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1));
+                       inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2));
+                       if (outformats & FORMAT_JSON)
+                               printf("\t\t{ \"ip_range\": { \"min\": \"%s\""
+                                   ", \"max\": \"%s\" }}", buf1, buf2);
+                       else
+                               printf("%5zu: IP: %s -- %s", i + j + 1, buf1,
+                                   buf2);
                        break;
                }
+               if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz)
+                       printf(",\n");
+               else
+                       printf("\n");
+       }
 
+       if (outformats & FORMAT_JSON)
+               printf("\t],\n");
 }
 
 void
@@ -172,81 +272,185 @@ crl_print(const struct crl *p)
        char *serial;
        time_t t;
 
-       printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
+       if (outformats & FORMAT_JSON) {
+               printf("\t\"type\": \"crl\",\n");
+               printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
+       } else
+               printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
 
        crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL);
        serial = x509_convert_seqnum(__func__, crlnum);
-       if (serial != NULL)
-               printf("CRL Serial Number: %s\n", serial);
+       if (serial != NULL) {
+               if (outformats & FORMAT_JSON)
+                       printf("\t\"crl_serial\": \"%s\",\n", serial);
+               else
+                       printf("CRL Serial Number: %s\n", serial);
+       }
        free(serial);
        ASN1_INTEGER_free(crlnum);
 
-       printf("CRL valid since: %s\n", time2str(p->issued));
-       printf("CRL valid until: %s\n", time2str(p->expires));
+       if (outformats & FORMAT_JSON) {
+               printf("\t\"valid_since\": %lld,\n", (long long)p->issued);
+               printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
+               printf("\t\"revoked_certs\": [\n");
+       } else {        
+               printf("CRL valid since: %s\n", time2str(p->issued));
+               printf("CRL valid until: %s\n", time2str(p->expires));
+               printf("Revoked Certificates:\n");
+       }
 
        revlist = X509_CRL_get_REVOKED(p->x509_crl);
        for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
-               if (i == 0)
-                       printf("Revoked Certificates:\n");
                rev = sk_X509_REVOKED_value(revlist, i);
-
                serial = x509_convert_seqnum(__func__,
                    X509_REVOKED_get0_serialNumber(rev));
                x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
-               if (serial != NULL)
-                       printf("    Serial: %8s   Revocation Date: %s\n",
-                           serial, time2str(t));
+               if (serial != NULL) {
+                       if (outformats & FORMAT_JSON) {
+                               printf("\t\t{ \"serial\": \"%s\"", serial);
+                               printf(", \"date\": \"%s\" }", time2str(t));
+                               if (i + 1 < sk_X509_REVOKED_num(revlist))
+                                       printf(",");
+                               printf("\n");
+                       } else
+                               printf("    Serial: %8s   Revocation Date: %s"
+                                   "\n", serial, time2str(t));
+               }
                free(serial);
        }
-       if (i == 0)
+
+       if (outformats & FORMAT_JSON)
+               printf("\t],\n");
+       else if (i == 0)
                printf("No Revoked Certificates\n");
 }
 
 void
-mft_print(const struct mft *p)
+mft_print(const X509 *x, const struct mft *p)
 {
        size_t i;
        char *hash;
 
-       printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
-       printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
-       printf("Authority info access: %s\n", p->aia);
-       printf("Manifest Number: %s\n", p->seqnum);
+       if (outformats & FORMAT_JSON) {
+               printf("\t\"type\": \"manifest\",\n");
+               printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
+               x509_print(x);
+               printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
+               printf("\t\"aia\": \"%s\",\n", p->aia);
+               printf("\t\"manifest_number\": \"%s\",\n", p->seqnum);
+               printf("\t\"valid_since\": %lld,\n", (long long)p->valid_since);
+               printf("\t\"valid_until\": %lld,\n", (long long)p->valid_until);
+       } else {
+               printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
+               printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
+               x509_print(x);
+               printf("Authority info access: %s\n", p->aia);
+               printf("Manifest Number: %s\n", p->seqnum);
+               printf("Manifest valid since: %s\n", time2str(p->valid_since));
+               printf("Manifest valid until: %s\n", time2str(p->valid_until));
+       }
+
        for (i = 0; i < p->filesz; i++) {
+               if (i == 0 && outformats & FORMAT_JSON)
+                       printf("\t\"filesandhashes\": [\n");
+
                if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
                    &hash) == -1)
                        errx(1, "base64_encode failure");
-               printf("%5zu: %s\n", i + 1, p->files[i].file);
-               printf("\thash %s\n", hash);
+
+               if (outformats & FORMAT_JSON) {
+                       printf("\t\t{ \"filename\": \"%s\",", p->files[i].file);
+                       printf(" \"hash\": \"%s\" }", hash);
+                       if (i + 1 < p->filesz)
+                               printf(",");
+                       printf("\n");
+               } else {
+                       printf("%5zu: %s\n", i + 1, p->files[i].file);
+                       printf("\thash %s\n", hash);
+               }
+
                free(hash);
        }
+
+       if (outformats & FORMAT_JSON)
+               printf("\t],\n");
+
+
 }
 
 void
-roa_print(const struct roa *p)
+roa_print(const X509 *x, const struct roa *p)
 {
        char     buf[128];
        size_t   i;
 
-       printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
-       printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
-       printf("Authority info access: %s\n", p->aia);
-       printf("ROA valid until: %s\n", time2str(p->expires));
+       if (outformats & FORMAT_JSON) {
+               printf("\t\"type\": \"roa\",\n");
+               printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
+               x509_print(x);
+               printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
+               printf("\t\"aia\": \"%s\",\n", p->aia);
+               printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
+       } else {
+               printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
+               x509_print(x);
+               printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
+               printf("Authority info access: %s\n", p->aia);
+               printf("ROA valid until: %s\n", time2str(p->expires));
+               printf("asID: %u\n", p->asid);
+       }
 
-       printf("asID: %u\n", p->asid);
        for (i = 0; i < p->ipsz; i++) {
+               if (i == 0 && outformats & FORMAT_JSON)
+                       printf("\t\"vrps\": [\n");
+
                ip_addr_print(&p->ips[i].addr,
                        p->ips[i].afi, buf, sizeof(buf));
-               printf("%5zu: %s maxlen: %hhu\n", i + 1,
-                       buf, p->ips[i].maxlength);
+
+               if (outformats & FORMAT_JSON) {
+                       printf("\t\t{ \"prefix\": \"%s\",", buf);
+                       printf(" \"asid\": %u,", p->asid);
+                       printf(" \"maxlen\": %hhu }", p->ips[i].maxlength);
+                       if (i + 1 < p->ipsz)
+                               printf(",");
+                       printf("\n");
+               } else
+                       printf("%5zu: %s maxlen: %hhu\n", i + 1, buf,
+                           p->ips[i].maxlength);
        }
+
+       if (outformats & FORMAT_JSON)
+               printf("\t],\n");
 }
 
 void
-gbr_print(const struct gbr *p)
+gbr_print(const X509 *x, const struct gbr *p)
 {
-       printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
-       printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
-       printf("Authority info access: %s\n", p->aia);
-       printf("vcard:\n%s", p->vcard);
+       size_t  i;
+
+       if (outformats & FORMAT_JSON) {
+               printf("\t\"type\": \"gbr\",\n");
+               printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
+               x509_print(x);
+               printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
+               printf("\t\"aia\": \"%s\",\n", p->aia);
+               printf("\t\"vcard\": \"");
+               for (i = 0; i < strlen(p->vcard); i++) {
+                       if (p->vcard[i] == '"')
+                               printf("\\\"");
+                       if (p->vcard[i] == '\r')
+                               continue;
+                       if (p->vcard[i] == '\n')
+                               printf("\\r\\n");
+                       else
+                               putchar(p->vcard[i]);
+               }
+               printf("\",\n");
+       } else {
+               printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
+               x509_print(x);
+               printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
+               printf("Authority info access: %s\n", p->aia);
+               printf("vcard:\n%s", p->vcard);
+       }
 }
index 4ff7728..b03188a 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: rpki-client.8,v 1.59 2022/04/12 12:54:09 jmc Exp $
+.\"    $OpenBSD: rpki-client.8,v 1.60 2022/04/20 10:46:20 job Exp $
 .\"
 .\" Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 12 2022 $
+.Dd $Mdocdate: April 20 2022 $
 .Dt RPKI-CLIENT 8
 .Os
 .Sh NAME
@@ -112,7 +112,11 @@ If
 .Ar file
 is an rsync:// URI, the corresponding file from the cache will be used.
 This option implies
-.Fl n .
+.Fl n ,
+and can be combined with
+.Fl j 
+to emit a stream of
+.Em Concatenated JSON .
 .It Fl j
 Create output in the file
 .Pa json