/* * Copyright (C) 2014 Dragon Research Labs ("DRL") * Portions copyright (C) 2006--2008 American Registry for Internet Numbers ("ARIN") * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notices and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND DRL AND ARIN DISCLAIM ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL OR * ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /* * Extract and test URIs from certificates. This is a unit test of * rcynic code, a utility, or both, depending on how it turns out. * * NB: OpenSSL insures that IA5 strings are null-terminated, so it's safe * for us to ignore the length count. */ #include #include #include #include #include #include #include #include #include #include #include static const unsigned char id_ad_caIssuers[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x2}; /* 1.3.6.1.5.5.7.48.2 */ static const unsigned char id_ad_caRepository[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x5}; /* 1.3.6.1.5.5.7.48.5 */ static const unsigned char id_ad_signedObjectRepository[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x9}; /* 1.3.6.1.5.5.7.48.9 */ static const unsigned char id_ad_rpkiManifest[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0xa}; /* 1.3.6.1.5.5.7.48.10 */ static const unsigned char id_ad_signedObject[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0xb}; /* 1.3.6.1.5.5.7.48.11 */ static X509 *read_cert(const char *filename, int format, int verbose) { BIO *b = BIO_new_file(filename, "r"); STACK_OF(X509) *certs = NULL; CMS_ContentInfo *cms = NULL; X509 *x = NULL; if (b == NULL) return NULL; switch (format) { case 'p': x = PEM_read_bio_X509(b, NULL, NULL, NULL); break; case 'd': x = d2i_X509_bio(b, NULL); break; } if (x == NULL) { BIO_reset(b); switch (format) { case 'p': cms = PEM_read_bio_CMS(b, NULL, NULL, NULL); break; case 'd': cms = d2i_CMS_bio(b, NULL); break; } if (cms != NULL && (certs = CMS_get1_certs(cms)) != NULL) x = sk_X509_shift(certs); } if (x != NULL && verbose) { X509_print_fp(stdout, x); printf("\n"); } sk_X509_pop_free(certs, X509_free); CMS_ContentInfo_free(cms); BIO_free(b); return x; } enum decode_errors { decode_ok, decode_no_extension, decode_not_exactly_one_DistributionPointName, decode_has_reasons, decode_has_CRLissuer, decode_no_distributionPoint, decode_not_GeneralName, decode_not_URI, }; static enum decode_errors decode_crldp(X509 *x, int verbose, int spaces) { enum decode_errors err = decode_ok; STACK_OF(DIST_POINT) *ds = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL); DIST_POINT *d; GENERAL_NAME *n; int i; if (!ds) { err = decode_no_extension; } else if (sk_DIST_POINT_num(ds) != 1) { err = decode_not_exactly_one_DistributionPointName; } else if ((d = sk_DIST_POINT_value(ds, 0))->reasons) { err = decode_has_reasons; } else if (d->CRLissuer) { err = decode_has_CRLissuer; } else if (!d->distpoint) { err = decode_no_distributionPoint; } else if (d->distpoint->type != 0) { err = decode_not_GeneralName; } else { for (i = 0; i < sk_GENERAL_NAME_num(d->distpoint->name.fullname); i++) { n = sk_GENERAL_NAME_value(d->distpoint->name.fullname, i); if (n->type != GEN_URI) { err = decode_not_GeneralName; break; } printf(" CRLDP: %s%s", n->d.uniformResourceIdentifier->data, spaces ? "" : "\n"); } } sk_DIST_POINT_pop_free(ds, DIST_POINT_free); return err; } #define decode_xia(_x_, _v_, _s_, _tag_, _nid_, _oid_) \ _decode_xia(_x_, _v_, _s_, _tag_, _nid_, _oid_, sizeof(_oid_)) static enum decode_errors _decode_xia(X509 *x, int verbose, int spaces, char *tag, int nid, const unsigned char *oid, int oidlen) { enum decode_errors err = decode_ok; AUTHORITY_INFO_ACCESS *as = X509_get_ext_d2i(x, nid, NULL, NULL); ACCESS_DESCRIPTION *a; int i; if (!as) { err = decode_no_extension; } else { for (i = 0; i < sk_ACCESS_DESCRIPTION_num(as); i++) { a = sk_ACCESS_DESCRIPTION_value(as, i); if (a->location->type != GEN_URI) { err = decode_not_URI; break; } if (a->method->length == oidlen && !memcmp(a->method->data, oid, oidlen)) printf(" %s: %s%s", tag, a->location->d.uniformResourceIdentifier->data, spaces ? "" : "\n"); } } sk_ACCESS_DESCRIPTION_pop_free(as, ACCESS_DESCRIPTION_free); return err; } const static struct option longopts[] = { { "der", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "pem", no_argument, NULL, 'p' }, { "spaces", no_argument, NULL, 's' }, { "verbose", no_argument, NULL, 'v' }, { NULL } }; static int usage (const char *jane, const int code) { FILE *out = code ? stderr : stdout; int i; fprintf(out, "usage: %s [-p | -d] cert [cert...]\n", jane); fprintf(out, "options:\n"); for (i = 0; longopts[i].name != NULL; i++) fprintf(out, " -%c --%s\n", longopts[i].val, longopts[i].name); return code; } int main(int argc, char *argv[]) { int c, format = 'd', spaces = 0, verbose = 0; const char *jane = argv[0]; X509 *x; OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); while ((c = getopt(argc, argv, "pdsv")) > 0) { switch (c) { case 'v': verbose = 1; break; case 'p': case 'd': format = c; break; case 's': spaces = 1; break; case 'h': return usage(jane, 0); default: return usage(jane, 1); } } argc -= optind; argv += optind; if (argc == 0) return usage(jane, 1); while (argc-- > 0) { printf(spaces ? "%s" : "File: %s\n", *argv); if ((x = read_cert(*argv++, format, verbose)) == NULL) { printf("Couldn't read certificate, skipping\n"); continue; } decode_xia(x, verbose, spaces, "AIA:caIssuers", NID_info_access, id_ad_caIssuers); decode_xia(x, verbose, spaces, "SIA:caRepository", NID_sinfo_access, id_ad_caRepository); decode_xia(x, verbose, spaces, "SIA:signedObjectRepository", NID_sinfo_access, id_ad_signedObjectRepository); decode_xia(x, verbose, spaces, "SIA:rpkiManifest", NID_sinfo_access, id_ad_rpkiManifest); decode_xia(x, verbose, spaces, "SIA:signedObject", NID_sinfo_access, id_ad_signedObject); decode_crldp(x, verbose, spaces); if (spaces) putchar('\n'); X509_free(x); } return 0; }