Use VICI 2.0 protocol version for certificate queries
[strongswan.git] / src / swanctl / commands / list_certs.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * Copyright (C) 2015 Andreas Steffen
6 * HSR Hochschule fuer Technik Rapperswil
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #define _GNU_SOURCE
20 #include <stdio.h>
21 #include <errno.h>
22 #include <time.h>
23
24 #include <asn1/asn1.h>
25 #include <asn1/oid.h>
26 #include <credentials/certificates/certificate.h>
27 #include <credentials/certificates/x509.h>
28 #include <credentials/certificates/crl.h>
29 #include <credentials/certificates/ac.h>
30 #include <selectors/traffic_selector.h>
31
32 #include <vici_version.h>
33 #include <vici_cert_info.h>
34
35 #include "command.h"
36
37 /**
38 * Current certificate type info
39 */
40 static vici_cert_info_t *current_cert_info = NULL;
41
42 /**
43 * Print PEM encoding of a certificate
44 */
45 static void print_pem(certificate_t *cert)
46 {
47 chunk_t encoding;
48
49 if (cert->get_encoding(cert, CERT_PEM, &encoding))
50 {
51 printf("%.*s", (int)encoding.len, encoding.ptr);
52 free(encoding.ptr);
53 }
54 else
55 {
56 fprintf(stderr, "PEM encoding certificate failed\n");
57 }
58 }
59
60 /**
61 * Print public key information
62 */
63 static void print_pubkey(public_key_t *key, bool has_privkey)
64 {
65 chunk_t chunk;
66
67 printf("pubkey: %N %d bits", key_type_names, key->get_type(key),
68 key->get_keysize(key));
69 if (has_privkey)
70 {
71 printf(", has private key");
72 }
73 printf("\n");
74 if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &chunk))
75 {
76 printf("keyid: %#B\n", &chunk);
77 }
78 if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &chunk))
79 {
80 printf("subjkey: %#B\n", &chunk);
81 }
82 }
83
84 /**
85 * Print X509 specific certificate information
86 */
87 static void print_x509(x509_t *x509)
88 {
89 enumerator_t *enumerator;
90 identification_t *id;
91 traffic_selector_t *block;
92 chunk_t chunk;
93 bool first;
94 char *uri;
95 int len, explicit, inhibit;
96 x509_flag_t flags;
97 x509_cdp_t *cdp;
98 x509_cert_policy_t *policy;
99 x509_policy_mapping_t *mapping;
100
101 chunk = chunk_skip_zero(x509->get_serial(x509));
102 printf("serial: %#B\n", &chunk);
103
104 first = TRUE;
105 enumerator = x509->create_subjectAltName_enumerator(x509);
106 while (enumerator->enumerate(enumerator, &id))
107 {
108 if (first)
109 {
110 printf("altNames: ");
111 first = FALSE;
112 }
113 else
114 {
115 printf(", ");
116 }
117 printf("%Y", id);
118 }
119 if (!first)
120 {
121 printf("\n");
122 }
123 enumerator->destroy(enumerator);
124
125 flags = x509->get_flags(x509);
126 if (flags != X509_NONE)
127 {
128 printf("flags: ");
129 if (flags & X509_CA)
130 {
131 printf("CA ");
132 }
133 if (flags & X509_CRL_SIGN)
134 {
135 printf("CRLSign ");
136 }
137 if (flags & X509_OCSP_SIGNER)
138 {
139 printf("ocspSigning ");
140 }
141 if (flags & X509_SERVER_AUTH)
142 {
143 printf("serverAuth ");
144 }
145 if (flags & X509_CLIENT_AUTH)
146 {
147 printf("clientAuth ");
148 }
149 if (flags & X509_IKE_INTERMEDIATE)
150 {
151 printf("ikeIntermediate ");
152 }
153 if (flags & X509_MS_SMARTCARD_LOGON)
154 {
155 printf("msSmartcardLogon");
156 }
157 if (flags & X509_SELF_SIGNED)
158 {
159 printf("self-signed ");
160 }
161 printf("\n");
162 }
163
164 first = TRUE;
165 enumerator = x509->create_crl_uri_enumerator(x509);
166 while (enumerator->enumerate(enumerator, &cdp))
167 {
168 if (first)
169 {
170 printf("CRL URIs: %s", cdp->uri);
171 first = FALSE;
172 }
173 else
174 {
175 printf(" %s", cdp->uri);
176 }
177 if (cdp->issuer)
178 {
179 printf(" (CRL issuer: %Y)", cdp->issuer);
180 }
181 printf("\n");
182 }
183 enumerator->destroy(enumerator);
184
185 first = TRUE;
186 enumerator = x509->create_ocsp_uri_enumerator(x509);
187 while (enumerator->enumerate(enumerator, &uri))
188 {
189 if (first)
190 {
191 printf("OCSP URIs: %s\n", uri);
192 first = FALSE;
193 }
194 else
195 {
196 printf(" %s\n", uri);
197 }
198 }
199 enumerator->destroy(enumerator);
200
201 len = x509->get_constraint(x509, X509_PATH_LEN);
202 if (len != X509_NO_CONSTRAINT)
203 {
204 printf("pathlen: %d\n", len);
205 }
206
207 first = TRUE;
208 enumerator = x509->create_name_constraint_enumerator(x509, TRUE);
209 while (enumerator->enumerate(enumerator, &id))
210 {
211 if (first)
212 {
213 printf("Permitted NameConstraints:\n");
214 first = FALSE;
215 }
216 printf(" %Y\n", id);
217 }
218 enumerator->destroy(enumerator);
219 first = TRUE;
220 enumerator = x509->create_name_constraint_enumerator(x509, FALSE);
221 while (enumerator->enumerate(enumerator, &id))
222 {
223 if (first)
224 {
225 printf("Excluded NameConstraints:\n");
226 first = FALSE;
227 }
228 printf(" %Y\n", id);
229 }
230 enumerator->destroy(enumerator);
231
232 first = TRUE;
233 enumerator = x509->create_cert_policy_enumerator(x509);
234 while (enumerator->enumerate(enumerator, &policy))
235 {
236 char *oid;
237
238 if (first)
239 {
240 printf("CertificatePolicies:\n");
241 first = FALSE;
242 }
243 oid = asn1_oid_to_string(policy->oid);
244 if (oid)
245 {
246 printf(" %s\n", oid);
247 free(oid);
248 }
249 else
250 {
251 printf(" %#B\n", &policy->oid);
252 }
253 if (policy->cps_uri)
254 {
255 printf(" CPS: %s\n", policy->cps_uri);
256 }
257 if (policy->unotice_text)
258 {
259 printf(" Notice: %s\n", policy->unotice_text);
260
261 }
262 }
263 enumerator->destroy(enumerator);
264
265 first = TRUE;
266 enumerator = x509->create_policy_mapping_enumerator(x509);
267 while (enumerator->enumerate(enumerator, &mapping))
268 {
269 char *issuer_oid, *subject_oid;
270
271 if (first)
272 {
273 printf("PolicyMappings:\n");
274 first = FALSE;
275 }
276 issuer_oid = asn1_oid_to_string(mapping->issuer);
277 subject_oid = asn1_oid_to_string(mapping->subject);
278 printf(" %s => %s\n", issuer_oid, subject_oid);
279 free(issuer_oid);
280 free(subject_oid);
281 }
282 enumerator->destroy(enumerator);
283
284 explicit = x509->get_constraint(x509, X509_REQUIRE_EXPLICIT_POLICY);
285 inhibit = x509->get_constraint(x509, X509_INHIBIT_POLICY_MAPPING);
286 len = x509->get_constraint(x509, X509_INHIBIT_ANY_POLICY);
287
288 if (explicit != X509_NO_CONSTRAINT || inhibit != X509_NO_CONSTRAINT ||
289 len != X509_NO_CONSTRAINT)
290 {
291 printf("PolicyConstraints:\n");
292 if (explicit != X509_NO_CONSTRAINT)
293 {
294 printf(" requireExplicitPolicy: %d\n", explicit);
295 }
296 if (inhibit != X509_NO_CONSTRAINT)
297 {
298 printf(" inhibitPolicyMapping: %d\n", inhibit);
299 }
300 if (len != X509_NO_CONSTRAINT)
301 {
302 printf(" inhibitAnyPolicy: %d\n", len);
303 }
304 }
305
306 chunk = x509->get_authKeyIdentifier(x509);
307 if (chunk.ptr)
308 {
309 printf("authkeyId: %#B\n", &chunk);
310 }
311
312 chunk = x509->get_subjectKeyIdentifier(x509);
313 if (chunk.ptr)
314 {
315 printf("subjkeyId: %#B\n", &chunk);
316 }
317 if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS)
318 {
319 first = TRUE;
320 printf("addresses: ");
321 enumerator = x509->create_ipAddrBlock_enumerator(x509);
322 while (enumerator->enumerate(enumerator, &block))
323 {
324 if (first)
325 {
326 first = FALSE;
327 }
328 else
329 {
330 printf(", ");
331 }
332 printf("%R", block);
333 }
334 enumerator->destroy(enumerator);
335 printf("\n");
336 }
337 }
338
339 /**
340 * Print CRL specific information
341 */
342 static void print_crl(crl_t *crl)
343 {
344 enumerator_t *enumerator;
345 time_t ts;
346 crl_reason_t reason;
347 chunk_t chunk;
348 int count = 0;
349 bool first;
350 char buf[64];
351 struct tm tm;
352 x509_cdp_t *cdp;
353
354 chunk = chunk_skip_zero(crl->get_serial(crl));
355 printf("serial: %#B\n", &chunk);
356
357 if (crl->is_delta_crl(crl, &chunk))
358 {
359 chunk = chunk_skip_zero(chunk);
360 printf("delta CRL: for serial %#B\n", &chunk);
361 }
362 chunk = crl->get_authKeyIdentifier(crl);
363 printf("authKeyId: %#B\n", &chunk);
364
365 first = TRUE;
366 enumerator = crl->create_delta_crl_uri_enumerator(crl);
367 while (enumerator->enumerate(enumerator, &cdp))
368 {
369 if (first)
370 {
371 printf("freshest: %s", cdp->uri);
372 first = FALSE;
373 }
374 else
375 {
376 printf(" %s", cdp->uri);
377 }
378 if (cdp->issuer)
379 {
380 printf(" (CRL issuer: %Y)", cdp->issuer);
381 }
382 printf("\n");
383 }
384 enumerator->destroy(enumerator);
385
386 enumerator = crl->create_enumerator(crl);
387 while (enumerator->enumerate(enumerator, &chunk, &ts, &reason))
388 {
389 count++;
390 }
391 enumerator->destroy(enumerator);
392
393 printf("%d revoked certificate%s%s\n", count,
394 count == 1 ? "" : "s", count ? ":" : "");
395 enumerator = crl->create_enumerator(crl);
396 while (enumerator->enumerate(enumerator, &chunk, &ts, &reason))
397 {
398 chunk = chunk_skip_zero(chunk);
399 localtime_r(&ts, &tm);
400 strftime(buf, sizeof(buf), "%F %T", &tm);
401 printf(" %#B: %s, %N\n", &chunk, buf, crl_reason_names, reason);
402 count++;
403 }
404 enumerator->destroy(enumerator);
405 }
406
407 /**
408 * Print AC specific information
409 */
410 static void print_ac(ac_t *ac)
411 {
412 ac_group_type_t type;
413 identification_t *id;
414 enumerator_t *groups;
415 chunk_t chunk;
416 bool first = TRUE;
417
418 chunk = chunk_skip_zero(ac->get_serial(ac));
419 printf("serial: %#B\n", &chunk);
420
421 id = ac->get_holderIssuer(ac);
422 if (id)
423 {
424 printf("hissuer: \"%Y\"\n", id);
425 }
426 chunk = chunk_skip_zero(ac->get_holderSerial(ac));
427 if (chunk.ptr)
428 {
429 printf("hserial: %#B\n", &chunk);
430 }
431 groups = ac->create_group_enumerator(ac);
432 while (groups->enumerate(groups, &type, &chunk))
433 {
434 int oid;
435 char *str;
436
437 if (first)
438 {
439 printf("groups: ");
440 first = FALSE;
441 }
442 else
443 {
444 printf(" ");
445 }
446 switch (type)
447 {
448 case AC_GROUP_TYPE_STRING:
449 printf("%.*s", (int)chunk.len, chunk.ptr);
450 break;
451 case AC_GROUP_TYPE_OID:
452 oid = asn1_known_oid(chunk);
453 if (oid == OID_UNKNOWN)
454 {
455 str = asn1_oid_to_string(chunk);
456 if (str)
457 {
458 printf("%s", str);
459 free(str);
460 }
461 else
462 {
463 printf("OID:%#B", &chunk);
464 }
465 }
466 else
467 {
468 printf("%s", oid_names[oid].name);
469 }
470 break;
471 case AC_GROUP_TYPE_OCTETS:
472 printf("%#B", &chunk);
473 break;
474 }
475 printf("\n");
476 }
477 groups->destroy(groups);
478
479 chunk = ac->get_authKeyIdentifier(ac);
480 if (chunk.ptr)
481 {
482 printf("authkey: %#B\n", &chunk);
483 }
484 }
485
486 /**
487 * Print certificate information
488 */
489 static void print_cert(certificate_t *cert, bool has_privkey)
490 {
491 time_t now, notAfter, notBefore;
492 public_key_t *key;
493
494 now = time(NULL);
495
496 if (cert->get_type(cert) != CERT_X509_CRL &&
497 cert->get_type(cert) != CERT_X509_OCSP_RESPONSE)
498 {
499 printf("subject: \"%Y\"\n", cert->get_subject(cert));
500 }
501 printf("issuer: \"%Y\"\n", cert->get_issuer(cert));
502
503 cert->get_validity(cert, &now, &notBefore, &notAfter);
504 printf("validity: not before %T, ", &notBefore, FALSE);
505 if (now < notBefore)
506 {
507 printf("not valid yet (valid in %V)\n", &now, &notBefore);
508 }
509 else
510 {
511 printf("ok\n");
512 }
513 printf(" not after %T, ", &notAfter, FALSE);
514 if (now > notAfter)
515 {
516 printf("expired (%V ago)\n", &now, &notAfter);
517 }
518 else
519 {
520 printf("ok (expires in %V)\n", &now, &notAfter);
521 }
522
523 switch (cert->get_type(cert))
524 {
525 case CERT_X509:
526 print_x509((x509_t*)cert);
527 break;
528 case CERT_X509_CRL:
529 print_crl((crl_t*)cert);
530 break;
531 case CERT_X509_AC:
532 print_ac((ac_t*)cert);
533 break;
534 default:
535 fprintf(stderr, "parsing certificate subtype %N not implemented\n",
536 certificate_type_names, cert->get_type(cert));
537 break;
538 }
539 key = cert->get_public_key(cert);
540 if (key)
541 {
542 print_pubkey(key, has_privkey);
543 key->destroy(key);
544 }
545 printf("\n");
546 }
547
548 CALLBACK(list_cb, void,
549 command_format_options_t *format, char *name, vici_res_t *res)
550 {
551 certificate_t *cert;
552 vici_version_t version;
553 vici_cert_info_t *cert_info;
554 bool has_privkey, first = FALSE;
555 char *version_str, *type_str;
556 void *buf;
557 int len;
558
559 if (*format & COMMAND_FORMAT_RAW)
560 {
561 vici_dump(res, "list-cert event", *format & COMMAND_FORMAT_PRETTY,
562 stdout);
563 return;
564 }
565
566 version_str = vici_find_str(res, "1.0", "vici");
567 if (!enum_from_name(vici_version_names, version_str, &version) ||
568 version == VICI_1_0)
569 {
570 fprintf(stderr, "unsupported vici version '%s'\n", version_str);
571 return;
572 }
573
574 buf = vici_find(res, &len, "data");
575 if (!buf)
576 {
577 fprintf(stderr, "received incomplete certificate data\n");
578 return;
579 }
580 has_privkey = streq(vici_find_str(res, "no", "has_privkey"), "yes");
581
582 type_str = vici_find_str(res, "any", "type");
583 cert_info = vici_cert_info_retrieve(type_str);
584 if (!cert_info || cert_info->type == CERT_ANY)
585 {
586 fprintf(stderr, "unsupported certificate type '%s'\n", type_str);
587 return;
588 }
589
590 /* Detect change of certificate type */
591 if (cert_info != current_cert_info)
592 {
593 first = TRUE;
594 current_cert_info = cert_info;
595 }
596
597 /* Parse certificate data blob */
598 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, cert_info->type,
599 BUILD_BLOB_ASN1_DER, chunk_create(buf, len),
600 BUILD_END);
601 if (cert)
602 {
603 if (*format & COMMAND_FORMAT_PEM)
604 {
605 print_pem(cert);
606 }
607 else
608 {
609 if (first)
610 {
611 printf("List of %ss:\n\n", cert_info->caption);
612 }
613 print_cert(cert, has_privkey);
614 }
615 cert->destroy(cert);
616 }
617 else
618 {
619 fprintf(stderr, "parsing certificate failed\n");
620 }
621 }
622
623 static int list_certs(vici_conn_t *conn)
624 {
625 vici_req_t *req;
626 vici_res_t *res;
627 command_format_options_t format = COMMAND_FORMAT_NONE;
628 char *arg, *subject = NULL, *type = NULL;
629 int ret;
630
631 while (TRUE)
632 {
633 switch (command_getopt(&arg))
634 {
635 case 'h':
636 return command_usage(NULL);
637 case 's':
638 subject = arg;
639 continue;
640 case 't':
641 type = arg;
642 continue;
643 case 'p':
644 format |= COMMAND_FORMAT_PEM;
645 continue;
646 case 'P':
647 format |= COMMAND_FORMAT_PRETTY;
648 /* fall through to raw */
649 case 'r':
650 format |= COMMAND_FORMAT_RAW;
651 continue;
652 case EOF:
653 break;
654 default:
655 return command_usage("invalid --list-certs option");
656 }
657 break;
658 }
659 if (vici_register(conn, "list-cert", list_cb, &format) != 0)
660 {
661 ret = errno;
662 fprintf(stderr, "registering for certificates failed: %s\n",
663 strerror(errno));
664 return ret;
665 }
666 req = vici_begin("list-certs");
667 vici_add_version(req, VICI_VERSION);
668
669 if (type)
670 {
671 vici_add_key_valuef(req, "type", "%s", type);
672 }
673 if (subject)
674 {
675 vici_add_key_valuef(req, "subject", "%s", subject);
676 }
677
678 res = vici_submit(req, conn);
679 if (!res)
680 {
681 ret = errno;
682 fprintf(stderr, "list-certs request failed: %s\n", strerror(errno));
683 return ret;
684 }
685 if (format & COMMAND_FORMAT_RAW)
686 {
687 vici_dump(res, "list-certs reply", format & COMMAND_FORMAT_PRETTY,
688 stdout);
689 }
690 vici_free_res(res);
691 return 0;
692 }
693
694 /**
695 * Register the command.
696 */
697 static void __attribute__ ((constructor))reg()
698 {
699 command_register((command_t) {
700 list_certs, 'x', "list-certs", "list stored certificates",
701 {"[--subject <dn/san>] "
702 "[--type x509|x509ca|x509aa|x509ac|x509crl|x509ocsp|ocsp] "
703 "[--pem] [--raw|--pretty]"},
704 {
705 {"help", 'h', 0, "show usage information"},
706 {"subject", 's', 1, "filter by certificate subject"},
707 {"type", 't', 1, "filter by certificate type"},
708 {"pem", 'p', 0, "print PEM encoding of certificate"},
709 {"raw", 'r', 0, "dump raw response message"},
710 {"pretty", 'P', 0, "dump raw response message in pretty print"},
711 }
712 });
713 }