1 /* $OpenBSD: print.c,v 1.14 2022/07/14 13:24:56 job Exp $ */ 2 /* 3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <arpa/inet.h> 22 23 #include <err.h> 24 #include <stdio.h> 25 #include <string.h> 26 #include <time.h> 27 28 #include <openssl/evp.h> 29 30 #include "extern.h" 31 32 static const char * 33 pretty_key_id(const char *hex) 34 { 35 static char buf[128]; /* bigger than SHA_DIGEST_LENGTH * 3 */ 36 size_t i; 37 38 for (i = 0; i < sizeof(buf) && *hex != '\0'; i++) { 39 if (i % 3 == 2) 40 buf[i] = ':'; 41 else 42 buf[i] = *hex++; 43 } 44 if (i == sizeof(buf)) 45 memcpy(buf + sizeof(buf) - 4, "...", 4); 46 else 47 buf[i] = '\0'; 48 return buf; 49 } 50 51 char * 52 time2str(time_t t) 53 { 54 static char buf[64]; 55 struct tm tm; 56 57 if (gmtime_r(&t, &tm) == NULL) 58 return "could not convert time"; 59 60 strftime(buf, sizeof(buf), "%h %d %T %Y %Z", &tm); 61 return buf; 62 } 63 64 void 65 tal_print(const struct tal *p) 66 { 67 char *ski; 68 EVP_PKEY *pk; 69 RSA *r; 70 const unsigned char *der; 71 unsigned char *rder = NULL; 72 unsigned char md[SHA_DIGEST_LENGTH]; 73 int rder_len; 74 size_t i; 75 76 der = p->pkey; 77 pk = d2i_PUBKEY(NULL, &der, p->pkeysz); 78 if (pk == NULL) 79 errx(1, "d2i_PUBKEY failed in %s", __func__); 80 81 r = EVP_PKEY_get0_RSA(pk); 82 if (r == NULL) 83 errx(1, "EVP_PKEY_get0_RSA failed in %s", __func__); 84 if ((rder_len = i2d_RSAPublicKey(r, &rder)) <= 0) 85 errx(1, "i2d_RSAPublicKey failed in %s", __func__); 86 87 if (!EVP_Digest(rder, rder_len, md, NULL, EVP_sha1(), NULL)) 88 errx(1, "EVP_Digest failed in %s", __func__); 89 90 ski = hex_encode(md, SHA_DIGEST_LENGTH); 91 92 if (outformats & FORMAT_JSON) { 93 printf("\t\"type\": \"tal\",\n"); 94 printf("\t\"name\": \"%s\",\n", p->descr); 95 printf("\t\"ski\": \"%s\",\n", pretty_key_id(ski)); 96 printf("\t\"trust_anchor_locations\": ["); 97 for (i = 0; i < p->urisz; i++) { 98 printf("\"%s\"", p->uri[i]); 99 if (i + 1 < p->urisz) 100 printf(", "); 101 } 102 printf("],\n"); 103 } else { 104 printf("Trust anchor name: %s\n", p->descr); 105 printf("Subject key identifier: %s\n", pretty_key_id(ski)); 106 printf("Trust anchor locations:\n"); 107 for (i = 0; i < p->urisz; i++) 108 printf("%5zu: %s\n", i + 1, p->uri[i]); 109 } 110 111 EVP_PKEY_free(pk); 112 free(rder); 113 free(ski); 114 } 115 116 void 117 x509_print(const X509 *x) 118 { 119 const ASN1_INTEGER *xserial; 120 char *serial = NULL; 121 122 xserial = X509_get0_serialNumber(x); 123 if (xserial == NULL) { 124 warnx("X509_get0_serialNumber failed in %s", __func__); 125 goto out; 126 } 127 128 serial = x509_convert_seqnum(__func__, xserial); 129 if (serial == NULL) { 130 warnx("x509_convert_seqnum failed in %s", __func__); 131 goto out; 132 } 133 134 if (outformats & FORMAT_JSON) { 135 printf("\t\"cert_serial\": \"%s\",\n", serial); 136 } else { 137 printf("Certificate serial: %s\n", serial); 138 } 139 140 out: 141 free(serial); 142 } 143 144 void 145 cert_print(const struct cert *p) 146 { 147 size_t i, j; 148 char buf1[64], buf2[64]; 149 int sockt; 150 char tbuf[21]; 151 152 strftime(tbuf, sizeof(tbuf), "%FT%TZ", gmtime(&p->expires)); 153 154 if (outformats & FORMAT_JSON) { 155 if (p->pubkey != NULL) 156 printf("\t\"type\": \"router_key\",\n"); 157 else 158 printf("\t\"type\": \"ca_cert\",\n"); 159 printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 160 if (p->aki != NULL) 161 printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 162 x509_print(p->x509); 163 if (p->aia != NULL) 164 printf("\t\"aia\": \"%s\",\n", p->aia); 165 if (p->mft != NULL) 166 printf("\t\"manifest\": \"%s\",\n", p->mft); 167 if (p->repo != NULL) 168 printf("\t\"carepository\": \"%s\",\n", p->repo); 169 if (p->notify != NULL) 170 printf("\t\"notify_url\": \"%s\",\n", p->notify); 171 if (p->pubkey != NULL) 172 printf("\t\"router_key\": \"%s\",\n", p->pubkey); 173 printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 174 printf("\t\"subordinate_resources\": [\n"); 175 } else { 176 printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 177 if (p->aki != NULL) 178 printf("Authority key identifier: %s\n", 179 pretty_key_id(p->aki)); 180 x509_print(p->x509); 181 if (p->aia != NULL) 182 printf("Authority info access: %s\n", p->aia); 183 if (p->mft != NULL) 184 printf("Manifest: %s\n", p->mft); 185 if (p->repo != NULL) 186 printf("caRepository: %s\n", p->repo); 187 if (p->notify != NULL) 188 printf("Notify URL: %s\n", p->notify); 189 if (p->pubkey != NULL) 190 printf("BGPsec P-256 ECDSA public key: %s\n", 191 p->pubkey); 192 printf("Valid until: %s\n", tbuf); 193 printf("Subordinate Resources:\n"); 194 } 195 196 for (i = 0; i < p->asz; i++) { 197 switch (p->as[i].type) { 198 case CERT_AS_ID: 199 if (outformats & FORMAT_JSON) 200 printf("\t\t{ \"asid\": %u }", p->as[i].id); 201 else 202 printf("%5zu: AS: %u", i + 1, p->as[i].id); 203 break; 204 case CERT_AS_INHERIT: 205 if (outformats & FORMAT_JSON) 206 printf("\t\t{ \"asid_inherit\": \"true\" }"); 207 else 208 printf("%5zu: AS: inherit", i + 1); 209 break; 210 case CERT_AS_RANGE: 211 if (outformats & FORMAT_JSON) 212 printf("\t\t{ \"asrange\": { \"min\": %u, " 213 "\"max\": %u }}", p->as[i].range.min, 214 p->as[i].range.max); 215 else 216 printf("%5zu: AS: %u -- %u", i + 1, 217 p->as[i].range.min, p->as[i].range.max); 218 break; 219 } 220 if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz) 221 printf(",\n"); 222 else 223 printf("\n"); 224 } 225 226 for (j = 0; j < p->ipsz; j++) { 227 switch (p->ips[j].type) { 228 case CERT_IP_INHERIT: 229 if (outformats & FORMAT_JSON) 230 printf("\t\t{ \"ip_inherit\": \"true\" }"); 231 else 232 printf("%5zu: IP: inherit", i + j + 1); 233 break; 234 case CERT_IP_ADDR: 235 ip_addr_print(&p->ips[j].ip, 236 p->ips[j].afi, buf1, sizeof(buf1)); 237 if (outformats & FORMAT_JSON) 238 printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1); 239 else 240 printf("%5zu: IP: %s", i + j + 1, buf1); 241 break; 242 case CERT_IP_RANGE: 243 sockt = (p->ips[j].afi == AFI_IPV4) ? 244 AF_INET : AF_INET6; 245 inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1)); 246 inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2)); 247 if (outformats & FORMAT_JSON) 248 printf("\t\t{ \"ip_range\": { \"min\": \"%s\"" 249 ", \"max\": \"%s\" }}", buf1, buf2); 250 else 251 printf("%5zu: IP: %s -- %s", i + j + 1, buf1, 252 buf2); 253 break; 254 } 255 if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz) 256 printf(",\n"); 257 else 258 printf("\n"); 259 } 260 261 if (outformats & FORMAT_JSON) 262 printf("\t],\n"); 263 } 264 265 void 266 crl_print(const struct crl *p) 267 { 268 STACK_OF(X509_REVOKED) *revlist; 269 X509_REVOKED *rev; 270 ASN1_INTEGER *crlnum; 271 int i; 272 char *serial; 273 time_t t; 274 275 if (outformats & FORMAT_JSON) { 276 printf("\t\"type\": \"crl\",\n"); 277 printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 278 } else 279 printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 280 281 crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL); 282 serial = x509_convert_seqnum(__func__, crlnum); 283 if (serial != NULL) { 284 if (outformats & FORMAT_JSON) 285 printf("\t\"crl_serial\": \"%s\",\n", serial); 286 else 287 printf("CRL Serial Number: %s\n", serial); 288 } 289 free(serial); 290 ASN1_INTEGER_free(crlnum); 291 292 if (outformats & FORMAT_JSON) { 293 printf("\t\"valid_since\": %lld,\n", (long long)p->issued); 294 printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 295 printf("\t\"revoked_certs\": [\n"); 296 } else { 297 printf("CRL valid since: %s\n", time2str(p->issued)); 298 printf("CRL valid until: %s\n", time2str(p->expires)); 299 printf("Revoked Certificates:\n"); 300 } 301 302 revlist = X509_CRL_get_REVOKED(p->x509_crl); 303 for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) { 304 rev = sk_X509_REVOKED_value(revlist, i); 305 serial = x509_convert_seqnum(__func__, 306 X509_REVOKED_get0_serialNumber(rev)); 307 x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t); 308 if (serial != NULL) { 309 if (outformats & FORMAT_JSON) { 310 printf("\t\t{ \"serial\": \"%s\"", serial); 311 printf(", \"date\": \"%s\" }", time2str(t)); 312 if (i + 1 < sk_X509_REVOKED_num(revlist)) 313 printf(","); 314 printf("\n"); 315 } else 316 printf(" Serial: %8s Revocation Date: %s" 317 "\n", serial, time2str(t)); 318 } 319 free(serial); 320 } 321 322 if (outformats & FORMAT_JSON) 323 printf("\t],\n"); 324 else if (i == 0) 325 printf("No Revoked Certificates\n"); 326 } 327 328 void 329 mft_print(const X509 *x, const struct mft *p) 330 { 331 size_t i; 332 char *hash; 333 334 if (outformats & FORMAT_JSON) { 335 printf("\t\"type\": \"manifest\",\n"); 336 printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 337 x509_print(x); 338 printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 339 printf("\t\"aia\": \"%s\",\n", p->aia); 340 printf("\t\"manifest_number\": \"%s\",\n", p->seqnum); 341 printf("\t\"valid_since\": %lld,\n", (long long)p->valid_since); 342 printf("\t\"valid_until\": %lld,\n", (long long)p->valid_until); 343 } else { 344 printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 345 printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 346 x509_print(x); 347 printf("Authority info access: %s\n", p->aia); 348 printf("Manifest Number: %s\n", p->seqnum); 349 printf("Manifest valid since: %s\n", time2str(p->valid_since)); 350 printf("Manifest valid until: %s\n", time2str(p->valid_until)); 351 } 352 353 for (i = 0; i < p->filesz; i++) { 354 if (i == 0 && outformats & FORMAT_JSON) 355 printf("\t\"filesandhashes\": [\n"); 356 357 if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 358 &hash) == -1) 359 errx(1, "base64_encode failure"); 360 361 if (outformats & FORMAT_JSON) { 362 printf("\t\t{ \"filename\": \"%s\",", p->files[i].file); 363 printf(" \"hash\": \"%s\" }", hash); 364 if (i + 1 < p->filesz) 365 printf(","); 366 printf("\n"); 367 } else { 368 printf("%5zu: %s\n", i + 1, p->files[i].file); 369 printf("\thash %s\n", hash); 370 } 371 372 free(hash); 373 } 374 375 if (outformats & FORMAT_JSON) 376 printf("\t],\n"); 377 } 378 379 void 380 roa_print(const X509 *x, const struct roa *p) 381 { 382 char buf[128]; 383 size_t i; 384 385 if (outformats & FORMAT_JSON) { 386 printf("\t\"type\": \"roa\",\n"); 387 printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 388 x509_print(x); 389 printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 390 printf("\t\"aia\": \"%s\",\n", p->aia); 391 printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 392 } else { 393 printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 394 x509_print(x); 395 printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 396 printf("Authority info access: %s\n", p->aia); 397 printf("ROA valid until: %s\n", time2str(p->expires)); 398 printf("asID: %u\n", p->asid); 399 } 400 401 for (i = 0; i < p->ipsz; i++) { 402 if (i == 0 && outformats & FORMAT_JSON) 403 printf("\t\"vrps\": [\n"); 404 405 ip_addr_print(&p->ips[i].addr, 406 p->ips[i].afi, buf, sizeof(buf)); 407 408 if (outformats & FORMAT_JSON) { 409 printf("\t\t{ \"prefix\": \"%s\",", buf); 410 printf(" \"asid\": %u,", p->asid); 411 printf(" \"maxlen\": %hhu }", p->ips[i].maxlength); 412 if (i + 1 < p->ipsz) 413 printf(","); 414 printf("\n"); 415 } else 416 printf("%5zu: %s maxlen: %hhu\n", i + 1, buf, 417 p->ips[i].maxlength); 418 } 419 420 if (outformats & FORMAT_JSON) 421 printf("\t],\n"); 422 } 423 424 void 425 gbr_print(const X509 *x, const struct gbr *p) 426 { 427 size_t i; 428 429 if (outformats & FORMAT_JSON) { 430 printf("\t\"type\": \"gbr\",\n"); 431 printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 432 x509_print(x); 433 printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 434 printf("\t\"aia\": \"%s\",\n", p->aia); 435 printf("\t\"vcard\": \""); 436 for (i = 0; i < strlen(p->vcard); i++) { 437 if (p->vcard[i] == '"') 438 printf("\\\""); 439 if (p->vcard[i] == '\r') 440 continue; 441 if (p->vcard[i] == '\n') 442 printf("\\r\\n"); 443 else 444 putchar(p->vcard[i]); 445 } 446 printf("\",\n"); 447 } else { 448 printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 449 x509_print(x); 450 printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 451 printf("Authority info access: %s\n", p->aia); 452 printf("vcard:\n%s", p->vcard); 453 } 454 } 455 456 void 457 rsc_print(const X509 *x, const struct rsc *p) 458 { 459 char buf1[64], buf2[64], tbuf[21]; 460 char *hash; 461 int sockt; 462 size_t i, j; 463 464 strftime(tbuf, sizeof(tbuf), "%FT%TZ", gmtime(&p->expires)); 465 466 if (outformats & FORMAT_JSON) { 467 printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 468 printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 469 x509_print(x); 470 printf("\t\"aia\": \"%s\",\n", p->aia); 471 printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 472 printf("\t\"signed_with_resources\": [\n"); 473 } else { 474 printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 475 printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 476 x509_print(x); 477 printf("Authority info access: %s\n", p->aia); 478 printf("Valid until: %s\n", tbuf); 479 printf("Signed with resources:\n"); 480 } 481 482 for (i = 0; i < p->asz; i++) { 483 switch (p->as[i].type) { 484 case CERT_AS_ID: 485 if (outformats & FORMAT_JSON) 486 printf("\t\t{ \"asid\": %u }", p->as[i].id); 487 else 488 printf("%5zu: AS: %u", i + 1, p->as[i].id); 489 break; 490 case CERT_AS_RANGE: 491 if (outformats & FORMAT_JSON) 492 printf("\t\t{ \"asrange\": { \"min\": %u, " 493 "\"max\": %u }}", p->as[i].range.min, 494 p->as[i].range.max); 495 else 496 printf("%5zu: AS: %u -- %u", i + 1, 497 p->as[i].range.min, p->as[i].range.max); 498 break; 499 case CERT_AS_INHERIT: 500 /* inheritance isn't possible in RSC */ 501 break; 502 } 503 if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz) 504 printf(",\n"); 505 else 506 printf("\n"); 507 } 508 509 for (j = 0; j < p->ipsz; j++) { 510 switch (p->ips[j].type) { 511 case CERT_IP_ADDR: 512 ip_addr_print(&p->ips[j].ip, 513 p->ips[j].afi, buf1, sizeof(buf1)); 514 if (outformats & FORMAT_JSON) 515 printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1); 516 else 517 printf("%5zu: IP: %s", i + j + 1, buf1); 518 break; 519 case CERT_IP_RANGE: 520 sockt = (p->ips[j].afi == AFI_IPV4) ? 521 AF_INET : AF_INET6; 522 inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1)); 523 inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2)); 524 if (outformats & FORMAT_JSON) 525 printf("\t\t{ \"ip_range\": { \"min\": \"%s\"" 526 ", \"max\": \"%s\" }}", buf1, buf2); 527 else 528 printf("%5zu: IP: %s -- %s", i + j + 1, buf1, 529 buf2); 530 break; 531 case CERT_IP_INHERIT: 532 /* inheritance isn't possible in RSC */ 533 break; 534 } 535 if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz) 536 printf(",\n"); 537 else 538 printf("\n"); 539 } 540 541 if (outformats & FORMAT_JSON) { 542 printf("\t],\n"); 543 printf("\t\"filenamesandhashes\": [\n"); 544 } else 545 printf("Filenames and hashes:\n"); 546 547 for (i = 0; i < p->filesz; i++) { 548 if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 549 &hash) == -1) 550 errx(1, "base64_encode failure"); 551 552 if (outformats & FORMAT_JSON) { 553 printf("\t\t{ \"filename\": \"%s\",", 554 p->files[i].filename ? p->files[i].filename : ""); 555 printf(" \"hash_digest\": \"%s\" }", hash); 556 if (i + 1 < p->filesz) 557 printf(","); 558 printf("\n"); 559 } else { 560 printf("%5zu: %s\n", i + 1, p->files[i].filename 561 ? p->files[i].filename : "no filename"); 562 printf("\thash %s\n", hash); 563 } 564 565 free(hash); 566 } 567 568 if (outformats & FORMAT_JSON) 569 printf("\t],\n"); 570 } 571