xref: /openbsd-src/usr.sbin/rpki-client/print.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
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