diff options
author | Rob Austein <sra@hactrn.net> | 2014-04-05 22:42:12 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2014-04-05 22:42:12 +0000 |
commit | fe0bf509f528dbdc50c7182f81057c6a4e15e4bd (patch) | |
tree | 07c9a923d4a0ccdfea11c49cd284f6d5757c5eda /rp/utils | |
parent | aa28ef54c271fbe4d52860ff8cf13cab19e2207c (diff) |
Source tree reorg, phase 1. Almost everything moved, no file contents changed.
svn path=/branches/tk685/; revision=5757
Diffstat (limited to 'rp/utils')
-rw-r--r-- | rp/utils/Makefile.in | 9 | ||||
-rw-r--r-- | rp/utils/README | 12 | ||||
-rw-r--r-- | rp/utils/dot.awk | 34 | ||||
-rw-r--r-- | rp/utils/find_roa.c | 356 | ||||
-rw-r--r-- | rp/utils/find_roa/Makefile.in | 56 | ||||
-rw-r--r-- | rp/utils/hashdir.c | 217 | ||||
-rw-r--r-- | rp/utils/hashdir/Makefile.in | 55 | ||||
-rw-r--r-- | rp/utils/print_roa.c | 384 | ||||
-rw-r--r-- | rp/utils/print_roa/Makefile.in | 52 | ||||
-rw-r--r-- | rp/utils/print_rpki_manifest.c | 235 | ||||
-rw-r--r-- | rp/utils/print_rpki_manifest/Makefile.in | 52 | ||||
-rw-r--r-- | rp/utils/scan_roas.c | 305 | ||||
-rw-r--r-- | rp/utils/scan_roas/Makefile.in | 52 | ||||
-rwxr-xr-x | rp/utils/scan_routercerts.py | 69 | ||||
-rw-r--r-- | rp/utils/scan_routercerts/Makefile.in | 41 | ||||
-rwxr-xr-x | rp/utils/strip_roa.sh | 39 | ||||
-rw-r--r-- | rp/utils/table.awk | 35 | ||||
-rw-r--r-- | rp/utils/test_roa.sh | 35 | ||||
-rw-r--r-- | rp/utils/uri.c | 248 | ||||
-rw-r--r-- | rp/utils/uri/Makefile.in | 31 |
20 files changed, 2317 insertions, 0 deletions
diff --git a/rp/utils/Makefile.in b/rp/utils/Makefile.in new file mode 100644 index 00000000..c89fdff5 --- /dev/null +++ b/rp/utils/Makefile.in @@ -0,0 +1,9 @@ +# $Id$ + +SUBDIRS = uri print_rpki_manifest print_roa hashdir find_roa scan_roas scan_routercerts + +all clean test distclean install deinstall uninstall:: + @for i in ${SUBDIRS}; do echo "Making $@ in $$i"; (cd $$i && ${MAKE} $@); done + +distclean:: + rm -f Makefile diff --git a/rp/utils/README b/rp/utils/README new file mode 100644 index 00000000..edbd793b --- /dev/null +++ b/rp/utils/README @@ -0,0 +1,12 @@ +$Id$ + +A collection of small RPKI utility programs which can be combined in +various useful ways by relying parties or by rpkid test scripts. + +See: + +- The primary documentation at http://trac.rpki.net/ + +- The PDF manual in ../doc/manual.pdf, or + +- The flat text page ../doc/doc.RPKI.Utils diff --git a/rp/utils/dot.awk b/rp/utils/dot.awk new file mode 100644 index 00000000..ca1b490b --- /dev/null +++ b/rp/utils/dot.awk @@ -0,0 +1,34 @@ +#!/usr/bin/awk -f +# $Id$ +# +# This doesn't really work right yet, and even if it did, the graph +# it would generate would be hopelessly large. + +BEGIN { + cmd = "find /var/rcynic/data/unauthenticated -type f -name '*.cer' -print0 | xargs -0 ./uri -d"; + while ((cmd | getline) == 1) { + if ($1 == "File") { + sub("/var/rcynic/data/unauthenticated/", "rsync://"); + u = $2; + uri[u] = ++n; + continue; + } + if ($1 == "SIA:") { + sia[u] = $2; + continue; + } + if ($1 == "AIA:") { + aia[u] = $2; + continue; + } + } + print "digraph rpki {"; + for (u in uri) { + printf "n%06d\t[ label=\"%s\" ];\n", uri[u], u; + if (sia[u]) + printf "n%06d -> n%06d\t [ color=blue ];\n", uri[u], uri[sia[u]]; + if (aia[u]) + printf "n%06d -> n%06d\t [ color=green ];\n", uri[u], uri[aia[u]]; + } + print "}"; +} diff --git a/rp/utils/find_roa.c b/rp/utils/find_roa.c new file mode 100644 index 00000000..a14242c8 --- /dev/null +++ b/rp/utils/find_roa.c @@ -0,0 +1,356 @@ +/* + * 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$ */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> + +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/safestack.h> +#include <openssl/conf.h> +#include <openssl/rand.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/cms.h> + +#include <rpki/roa.h> + +#ifndef FILENAME_MAX +#define FILENAME_MAX 1024 +#endif + +#ifndef ADDR_RAW_BUF_LEN +#define ADDR_RAW_BUF_LEN 16 +#endif + + + +/* + * Error handling. + */ + +#define _lose(_msg_, _file_) \ + do { \ + if (_file_) \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, _msg_, _file_); \ + else \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, _msg_); \ + fprintf(stderr, "%s: %s\n", _msg_, _file_); \ + } while (0) + +#define lose(_msg_, _file_) \ + do { \ + _lose(_msg_, _file_); \ + goto done; \ + } while (0) + +#define lose_errno(_msg_, _file_) \ + do { \ + _lose(_msg_, _file_); \ + perror(NULL); \ + goto done; \ + } while (0) + +#define lose_openssl(_msg_, _file_) \ + do { \ + _lose(_msg_, _file_); \ + ERR_print_errors_fp(stderr); \ + goto done; \ + } while (0) + + +/* + * Extract a ROA prefix from the ASN.1 bitstring encoding. + */ +static int extract_roa_prefix(unsigned char *addr, + unsigned *prefixlen, + const ASN1_BIT_STRING *bs, + const unsigned afi) +{ + unsigned length; + + switch (afi) { + case IANA_AFI_IPV4: length = 4; break; + case IANA_AFI_IPV6: length = 16; break; + default: return 0; + } + + if (bs->length < 0 || bs->length > length) + return 0; + + if (bs->length > 0) { + memcpy(addr, bs->data, bs->length); + if ((bs->flags & 7) != 0) { + unsigned char mask = 0xFF >> (8 - (bs->flags & 7)); + addr[bs->length - 1] &= ~mask; + } + } + + memset(addr + bs->length, 0, length - bs->length); + + *prefixlen = (bs->length * 8) - (bs->flags & 7); + + return 1; +} + +/* + * Check str for a trailing suffix. + */ +static int has_suffix(const char *str, const char *suffix) +{ + size_t len_str, len_suffix; + assert(str != NULL && suffix != NULL); + len_str = strlen(str); + len_suffix = strlen(suffix); + return len_str >= len_suffix && !strcmp(str + len_str - len_suffix, suffix); +} + +/* + * Handle one object. + */ +static void file_handler(const char *filename, const unsigned prefix_afi, const unsigned char *prefix, const unsigned long prefixlen) +{ + unsigned char roa_prefix[ADDR_RAW_BUF_LEN]; + unsigned roa_prefixlen, roa_maxprefixlen, plen; + CMS_ContentInfo *cms = NULL; + BIO *b = NULL; + ROA *r = NULL; + int i, j, k, n; + unsigned long asid; + + if (!(b = BIO_new_file(filename, "rb"))) + lose_openssl("Couldn't open CMS file", filename); + + if ((cms = d2i_CMS_bio(b, NULL)) == NULL) + lose_openssl("Couldn't read CMS file", filename); + + BIO_free(b); + + if ((b = BIO_new(BIO_s_mem())) == NULL) + lose_openssl("Couldn't open ROA", filename); + + if (CMS_verify(cms, NULL, NULL, NULL, b, CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY) <= 0) + lose_openssl("Couldn't parse ROA CMS", filename); + + if ((r = ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), b, NULL)) == NULL) + lose_openssl("Couldn't parse ROA", filename); + + asid = (unsigned long) ASN1_INTEGER_get(r->asID); + + for (i = 0; i < sk_ROAIPAddressFamily_num(r->ipAddrBlocks); i++) { + ROAIPAddressFamily *f = sk_ROAIPAddressFamily_value(r->ipAddrBlocks, i); + + /* + * AFI must match, SAFI must be null + */ + if (f->addressFamily->length != 2 || + prefix_afi != ((f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]))) + continue; + + for (j = 0; j < sk_ROAIPAddress_num(f->addresses); j++) { + ROAIPAddress *a = sk_ROAIPAddress_value(f->addresses, j); + + if (!extract_roa_prefix(roa_prefix, &roa_prefixlen, a->IPAddress, prefix_afi)) + lose("Malformed ROA", filename); + + /* + * If the prefix we're looking for is bigger than the ROA + * prefix, the ROA can't possibly cover. + */ + if (prefixlen < roa_prefixlen) + continue; + + if (a->maxLength) + roa_maxprefixlen = ASN1_INTEGER_get(a->maxLength); + else + roa_maxprefixlen = roa_prefixlen; + + /* + * If the prefix we're looking for is smaller than the smallest + * allowed slice of the ROA prefix, the ROA can't possibly + * cover. + */ + if (prefixlen > roa_maxprefixlen) + continue; + + /* + * If we get this far, we have to compare prefixes. + */ + assert(roa_prefixlen <= ADDR_RAW_BUF_LEN * 8); + plen = prefixlen < roa_prefixlen ? prefixlen : roa_prefixlen; + k = 0; + while (plen >= 8 && prefix[k] == roa_prefix[k]) { + plen -= 8; + k++; + } + if (plen > 8 || ((prefix[k] ^ roa_prefix[k]) & (0xFF << (8 - plen))) != 0) + continue; + + /* + * If we get here, we have a match. + */ + printf("ASN %lu prefix ", asid); + switch (prefix_afi) { + case IANA_AFI_IPV4: + printf("%u.%u.%u.%u", prefix[0], prefix[1], prefix[2], prefix[3]); + break; + case IANA_AFI_IPV6: + for (n = 16; n > 1 && prefix[n-1] == 0x00 && prefix[n-2] == 0x00; n -= 2) + ; + for (k = 0; k < n; k += 2) + printf("%x%s", (prefix[k] << 8) | prefix[k+1], (k < 14 ? ":" : "")); + if (k < 16) + printf(":"); + break; + } + printf("/%lu ROA %s\n", prefixlen, filename); + goto done; + } + } + + done: + BIO_free(b); + CMS_ContentInfo_free(cms); + ROA_free(r); +} + +/* + * Walk a directory tree + */ +static int handle_directory(const char *name, const unsigned prefix_afi, const unsigned char *prefix, const unsigned long prefixlen) +{ + char path[FILENAME_MAX]; + struct dirent *d; + size_t len; + DIR *dir; + int ret = 0, need_slash; + + assert(name); + len = strlen(name); + assert(len > 0 && len < sizeof(path)); + need_slash = name[len - 1] != '/'; + + if ((dir = opendir(name)) == NULL) + lose_errno("Couldn't open directory", name); + + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if (len + strlen(d->d_name) + need_slash >= sizeof(path)) + lose("Constructed path name too long", d->d_name); + strcpy(path, name); + if (need_slash) + strcat(path, "/"); + strcat(path, d->d_name); + switch (d->d_type) { + case DT_DIR: + if (!handle_directory(path, prefix_afi, prefix, prefixlen)) + lose("Directory walk failed", path); + continue; + default: + if (has_suffix(path, ".roa")) + file_handler(path, prefix_afi, prefix, prefixlen); + continue; + } + } + + ret = 1; + + done: + if (dir) + closedir(dir); + return ret; +} + +static void usage (const char *jane, const int code) +{ + fprintf(code ? stderr : stdout, "usage: %s authtree prefix [prefix...]\n", jane); + exit(code); +} + +int main (int argc, char *argv[]) +{ + unsigned char prefix[ADDR_RAW_BUF_LEN]; + unsigned long prefixlen; + unsigned afi; + char *s = NULL, *p = NULL; + int i, len, ret = 1; + + if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) + usage(argv[0], 0); + + if (argc < 3) + usage(argv[0], 1); + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + for (i = 2; i < argc; i++) { + + if ((s = strdup(argv[i])) == NULL) + lose("Couldn't strdup()", argv[i]); + + if ((p = strchr(s, '/')) != NULL) + *p++ = '\0'; + + len = a2i_ipadd(prefix, s); + + switch (len) { + case 4: afi = IANA_AFI_IPV4; break; + case 16: afi = IANA_AFI_IPV6; break; + default: lose("Unknown AFI", argv[i]); + } + + if (p) { + if (*p == '\0' || + (prefixlen = strtoul(p, &p, 10)) == ULONG_MAX || + *p != '\0' || + prefixlen > ADDR_RAW_BUF_LEN * 8) + lose("Bad prefix length", argv[i]); + } else { + prefixlen = len * 8; + } + + assert(prefixlen <= ADDR_RAW_BUF_LEN * 8); + + free(s); + p = s = NULL; + + if (!handle_directory(argv[1], afi, prefix, prefixlen)) + goto done; + + } + + ret = 0; + + done: + if (s) + free(s); + return ret; +} diff --git a/rp/utils/find_roa/Makefile.in b/rp/utils/find_roa/Makefile.in new file mode 100644 index 00000000..36c68e01 --- /dev/null +++ b/rp/utils/find_roa/Makefile.in @@ -0,0 +1,56 @@ +# $Id$ + +NAME = find_roa + +BIN = ${NAME} +SRC = ${NAME}.c +OBJ = ${NAME}.o + +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +INSTALL = @INSTALL@ -m 555 + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localstatedir = @localstatedir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +libdir = @libdir@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all: ${BIN} + +clean: + rm -rf ${BIN} ${OBJ} ${BIN}.dSYM + +${BIN}: ${SRC} + ${CC} ${CFLAGS} -o $@ ${SRC} ${LDFLAGS} ${LIBS} + + +ROA_DIR = ${abs_top_builddir}/rcynic/rcynic-data/authenticated + +TEST_ARGS = ${ROA_DIR} 10.3.0.44 10.2.0.6 10.0.0.0/24 + +test: ${BIN} +# if test -d ${ROA_DIR}; then ./${BIN} ${TEST_ARGS} ; else :; fi + if test -d ${ROA_DIR}; then sh ./test_roa.sh ${TEST_ARGS} ; else :; fi + +install: all + if test -d ${DESTDIR}${bindir} ; then :; else ${INSTALL} -d ${DESTDIR}${bindir}; fi + ${INSTALL} ${BIN} ${DESTDIR}${bindir} + +deinstall uninstall: + rm -f ${DESTDIR}${bindir}/${BIN} + +distclean: clean + rm -rf hashed-pem-dir + rm -f Makefile diff --git a/rp/utils/hashdir.c b/rp/utils/hashdir.c new file mode 100644 index 00000000..1b5b0f46 --- /dev/null +++ b/rp/utils/hashdir.c @@ -0,0 +1,217 @@ +/* + * 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$ */ + +/* + * Read a directory tree of DER certificates and CRLs and copy + * them into a PEM format directory with names in the hash format + * that OpenSSL's lookup routines expect. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> + +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/x509.h> +#include <openssl/err.h> + +#ifndef FILENAME_MAX +#define FILENAME_MAX 1024 +#endif + +static int verbose = 1; + +/* + * Error handling. + */ + +#define _lose(_msg_, _file_) \ + do { \ + fprintf(stderr, "%s: %s\n", _msg_, _file_); \ + } while (0) + +#define lose(_msg_, _file_) \ + do { \ + _lose(_msg_, _file_); \ + goto done; \ + } while (0) + +#define lose_errno(_msg_, _file_) \ + do { \ + _lose(_msg_, _file_); \ + perror(NULL); \ + goto done; \ + } while (0) + +#define lose_openssl(_msg_, _file_) \ + do { \ + _lose(_msg_, _file_); \ + ERR_print_errors_fp(stderr); \ + goto done; \ + } while (0) + +/* + * Check str for a trailing suffix. + */ +static int has_suffix(const char *str, const char *suffix) +{ + size_t len_str, len_suffix; + assert(str != NULL && suffix != NULL); + len_str = strlen(str); + len_suffix = strlen(suffix); + return len_str >= len_suffix && !strcmp(str + len_str - len_suffix, suffix); +} + +/* + * Handle one object. + */ +static void file_handler(const char *filename, const char *targetdir) +{ + char path[FILENAME_MAX]; + unsigned long hash; + const char *fmt; + X509_CRL *crl = NULL; + X509 *cer = NULL; + BIO *b = NULL; + int i, is_crl; + + if (has_suffix(filename, ".cer")) + is_crl = 0; + else if (has_suffix(filename, ".crl")) + is_crl = 1; + else + return; /* Ignore if neither certificate nor CRL */ + + if (verbose) + printf("Reading %s\n", filename); + + if (!(b = BIO_new_file(filename, "rb"))) + lose_openssl("Couldn't open input file", filename); + + if (is_crl + ? !(crl = d2i_X509_CRL_bio(b, NULL)) + : !(cer = d2i_X509_bio(b, NULL))) + lose_openssl("Couldn't read DER object", filename); + + BIO_free(b); + b = NULL; + + if (is_crl) { + hash = X509_NAME_hash(X509_CRL_get_issuer(crl)); + fmt = "%s/%08lx.r%d"; + } else { + hash = X509_subject_name_hash(cer); + fmt = "%s/%08lx.%d"; + } + + for (i = 0; i < INT_MAX; i++) + if (snprintf(path, sizeof(path), fmt, targetdir, hash, i) == sizeof(path)) + lose("Path too long", filename); + else if (access(path, F_OK)) + break; + if (i == INT_MAX) + lose("No pathname available", filename); + + if (verbose) + printf("Writing %s\n", path); + + if (!(b = BIO_new_file(path, "w"))) + lose_openssl("Couldn't open output file", path); + + if (is_crl + ? !PEM_write_bio_X509_CRL(b, crl) + : !PEM_write_bio_X509(b, cer)) + lose_openssl("Couldn't write PEM object", path); + + done: + X509_free(cer); + X509_CRL_free(crl); + BIO_free(b); +} + +/* + * Walk a directory tree + */ +static int handle_directory(const char *name, const char *targetdir) +{ + char path[FILENAME_MAX]; + struct dirent *d; + size_t len; + DIR *dir; + int ret = 0, need_slash; + + assert(name); + len = strlen(name); + assert(len > 0 && len < sizeof(path)); + need_slash = name[len - 1] != '/'; + + if ((dir = opendir(name)) == NULL) + lose_errno("Couldn't open directory", name); + + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if (len + strlen(d->d_name) + need_slash >= sizeof(path)) + lose("Constructed path name too long", d->d_name); + strcpy(path, name); + if (need_slash) + strcat(path, "/"); + strcat(path, d->d_name); + switch (d->d_type) { + case DT_DIR: + if (!handle_directory(path, targetdir)) + lose("Directory walk failed", path); + continue; + default: + file_handler(path, targetdir); + continue; + } + } + + ret = 1; + + done: + if (dir) + closedir(dir); + return ret; +} + +static void usage (const char *jane, const int code) +{ + fprintf(code ? stderr : stdout, "usage: %s input-directory output-directory\n", jane); + exit(code); +} + +int main(int argc, char *argv[]) +{ + if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) + usage(argv[0], 0); + + if (argc != 3) + usage(argv[0], 1); + + return !handle_directory(argv[1], argv[2]); +} diff --git a/rp/utils/hashdir/Makefile.in b/rp/utils/hashdir/Makefile.in new file mode 100644 index 00000000..c0cf448a --- /dev/null +++ b/rp/utils/hashdir/Makefile.in @@ -0,0 +1,55 @@ +# $Id$ + +NAME = hashdir + +BIN = ${NAME} +SRC = ${NAME}.c +OBJ = ${NAME}.o + +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +INSTALL = @INSTALL@ -m 555 + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localstatedir = @localstatedir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +libdir = @libdir@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all: ${BIN} + +clean:: + rm -rf ${BIN} ${OBJ} ${BIN}.dSYM + +${BIN}: ${SRC} + ${CC} ${CFLAGS} -o $@ ${SRC} ${LDFLAGS} ${LIBS} + +INPUT = ${abs_top_builddir}/rcynic/rcynic-data/authenticated +OUTPUT = hashed-pem-dir + +test: ${BIN} + if test -d ${INPUT}; then rm -rf ${OUTPUT} && mkdir ${OUTPUT} && ./hashdir ${INPUT} ${OUTPUT}; else :; fi + +clean:: + rm -rf ${OUTPUT} + +install: all + if test -d ${DESTDIR}${bindir} ; then :; else ${INSTALL} -d ${DESTDIR}${bindir}; fi + ${INSTALL} ${BIN} ${DESTDIR}${bindir} + +deinstall uninstall: + rm -f ${DESTDIR}${bindir}/${BIN} + +distclean: clean + rm -f Makefile diff --git a/rp/utils/print_roa.c b/rp/utils/print_roa.c new file mode 100644 index 00000000..c88fc092 --- /dev/null +++ b/rp/utils/print_roa.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2014 Dragon Research Labs ("DRL") + * Portions copyright (C) 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$ */ + +/* + * Decoder test for ROAs. + * + * NB: This does -not- check the CMS signatures, just the encoding. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> + +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/safestack.h> +#include <openssl/conf.h> +#include <openssl/rand.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/cms.h> + +#include <rpki/roa.h> + +/* + * How much buffer space do we need for a raw address? + */ +#define ADDR_RAW_BUF_LEN 16 + + + +/* + * Extract signing time from CMS message. + */ + +static char * +extract_signingTime(CMS_ContentInfo *cms, char *buffer, size_t buflen) +{ + STACK_OF(CMS_SignerInfo) *sis = NULL; + CMS_SignerInfo *si = NULL; + X509_ATTRIBUTE *xa = NULL; + ASN1_TYPE *so = NULL; + int i = -1; + + if (cms == NULL || + buffer == NULL || + buflen < sizeof("20010401123456Z") || + (sis = CMS_get0_SignerInfos(cms)) == NULL || + sk_CMS_SignerInfo_num(sis) != 1 || + (si = sk_CMS_SignerInfo_value(sis, 0)) < 0 || + (i = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)) < 0 || + (xa = CMS_signed_get_attr(si, i)) == NULL || + xa->single || + sk_ASN1_TYPE_num(xa->value.set) != 1 || + (so = sk_ASN1_TYPE_value(xa->value.set, 0)) == NULL) + return NULL; + + assert(buflen > 2); + buffer[buflen - 1] = '\0'; + + switch (so->type) { + case V_ASN1_UTCTIME: + strcpy(buffer, (so->value.utctime->data[0] >= '5') ? "19" : "20"); + return strncpy(buffer + 2, (const char *) so->value.utctime->data, buflen - 3); + case V_ASN1_GENERALIZEDTIME: + return strncpy(buffer, (const char *) so->value.generalizedtime->data, buflen - 1); + default: + return NULL; + } +} + + + +/* + * Expand the bitstring form of an address into a raw byte array. + * At the moment this is coded for simplicity, not speed. + */ +static void addr_expand(unsigned char *addr, + const ASN1_BIT_STRING *bs, + const int length) +{ + assert(bs->length >= 0 && bs->length <= length); + if (bs->length > 0) { + memcpy(addr, bs->data, bs->length); + if ((bs->flags & 7) != 0) { + unsigned char mask = 0xFF >> (8 - (bs->flags & 7)); + addr[bs->length - 1] &= ~mask; + } + } + memset(addr + bs->length, 0, length - bs->length); +} + +/* + * Extract the prefix length from a bitstring. + */ +#define addr_prefixlen(bs) ((int) ((bs)->length * 8 - ((bs)->flags & 7))) + +/* + * Read ROA (CMS object) in DER format. + * + * NB: When invoked this way, CMS_verify() does -not- verify, it just decodes the ASN.1. + * + * Well, OK, this function has evolved to doing a lot more than just + * reading the object. Refactor or at least rename, someday. + */ +static ROA *read_roa(const char *filename, + const int print_cms, + const int print_roa, + const int print_signerinfo, + const int print_brief, + const int print_signingtime) +{ + unsigned char addr[ADDR_RAW_BUF_LEN]; + CMS_ContentInfo *cms = NULL; + const ASN1_OBJECT *oid = NULL; + char *asID = NULL; + BIGNUM *bn = NULL; + ROA *r = NULL; + char buf[512]; + BIO *b = NULL; + int i, j, k, n; + + if ((b = BIO_new_file(filename, "r")) == NULL || + (cms = d2i_CMS_bio(b, NULL)) == NULL) + goto done; + BIO_free(b); + b = NULL; + + if (print_signerinfo) { + STACK_OF(CMS_SignerInfo) *signerInfos = CMS_get0_SignerInfos(cms); + STACK_OF(X509) *certs = CMS_get1_certs(cms); + STACK_OF(X509_CRL) *crls = CMS_get1_crls(cms); + printf("Certificates: %d\n", certs ? sk_X509_num(certs) : 0); + printf("CRLs: %d\n", crls ? sk_X509_CRL_num(crls) : 0); + for (i = 0; i < sk_CMS_SignerInfo_num(signerInfos); i++) { + CMS_SignerInfo *si = sk_CMS_SignerInfo_value(signerInfos, i); + ASN1_OCTET_STRING *hash = NULL; + printf("SignerId[%d]: ", i); + if (CMS_SignerInfo_get0_signer_id(si, &hash, NULL, NULL) && hash != NULL) + for (j = 0; j < hash->length; j++) + printf("%02x%s", hash->data[j], j == hash->length - 1 ? "" : ":"); + else + printf("[Could not read SID]"); + if (certs) + for (j = 0; j < sk_X509_num(certs); j++) + if (!CMS_SignerInfo_cert_cmp(si, sk_X509_value(certs, j))) + printf(" [Matches certificate %d]", j); + if ((j = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)) >= 0) { + X509_ATTRIBUTE *xa = CMS_signed_get_attr(si, j); + if (xa && !xa->single && sk_ASN1_TYPE_num(xa->value.set) == 1) { + ASN1_TYPE *so = sk_ASN1_TYPE_value(xa->value.set, 0); + switch (so->type) { + case V_ASN1_UTCTIME: + printf(" [signingTime(U) %s%s]", + so->value.utctime->data[0] < '5' ? "20" : "19", + so->value.utctime->data); + break; + case V_ASN1_GENERALIZEDTIME: + printf(" [signingTime(G) %s]", + so->value.generalizedtime->data); + break; + } + } + } + printf("\n"); + } + sk_X509_pop_free(certs, X509_free); + sk_X509_CRL_pop_free(crls, X509_CRL_free); + } + + if ((b = BIO_new(BIO_s_mem())) == NULL || + CMS_verify(cms, NULL, NULL, NULL, b, CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY) <= 0 || + (r = ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), b, NULL)) == NULL) + goto done; + BIO_free(b); + b = NULL; + + if (print_roa) { + + bn = ASN1_INTEGER_to_BN(r->asID, NULL); + asID = BN_bn2dec(bn); + + if (print_brief) { + + if (print_signingtime) { + char buffer[sizeof("20010401123456Z")], *b; + if (!extract_signingTime(cms, buffer, sizeof(buffer))) + goto done; + printf("%s ", buffer); + } + + fputs(asID, stdout); + + } else { + + if ((oid = CMS_get0_eContentType(cms)) == NULL) + goto done; + OBJ_obj2txt(buf, sizeof(buf), oid, 0); + printf("eContentType: %s\n", buf); + + if (r->version) + printf("version: %ld\n", ASN1_INTEGER_get(r->version)); + else + printf("version: 0 [Defaulted]\n"); + printf("asID: %s\n", asID); + } + + for (i = 0; i < sk_ROAIPAddressFamily_num(r->ipAddrBlocks); i++) { + + ROAIPAddressFamily *f = sk_ROAIPAddressFamily_value(r->ipAddrBlocks, i); + + unsigned afi = (f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]); + + if (!print_brief) { + printf(" addressFamily: %x", afi); + if (f->addressFamily->length == 3) + printf("[%x]", f->addressFamily->data[2]); + printf("\n"); + } + + for (j = 0; j < sk_ROAIPAddress_num(f->addresses); j++) { + ROAIPAddress *a = sk_ROAIPAddress_value(f->addresses, j); + + if (print_brief) + printf(" "); + else + printf(" IPaddress: "); + + switch (afi) { + + case IANA_AFI_IPV4: + addr_expand(addr, a->IPAddress, 4); + printf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + break; + + case IANA_AFI_IPV6: + addr_expand(addr, a->IPAddress, 16); + for (n = 16; n > 1 && addr[n-1] == 0x00 && addr[n-2] == 0x00; n -= 2) + ; + for (k = 0; k < n; k += 2) + printf("%x%s", (addr[k] << 8) | addr[k+1], (k < 14 ? ":" : "")); + if (k < 16) + printf(":"); + if (k == 0) + printf(":"); + break; + + default: + if (!print_brief) { + for (k = 0; k < a->IPAddress->length; k++) + printf("%s%02x", (k > 0 ? ":" : ""), a->IPAddress->data[k]); + printf("[%d]", (int) (a->IPAddress->flags & 7)); + } + break; + + } + + printf("/%u", addr_prefixlen(a->IPAddress)); + + if (a->maxLength) + printf("-%ld", ASN1_INTEGER_get(a->maxLength)); + + if (!print_brief) + printf("\n"); + } + } + if (print_brief) + printf("\n"); + } + + if (print_cms) { + if (print_roa) + printf("\n"); + fflush(stdout); + if ((b = BIO_new(BIO_s_fd())) == NULL) + goto done; + BIO_set_fd(b, 1, BIO_NOCLOSE); + CMS_ContentInfo_print_ctx(b, cms, 0, NULL); + BIO_free(b); + b = NULL; + } + + done: + if (ERR_peek_error()) + ERR_print_errors_fp(stderr); + BIO_free(b); + BN_free(bn); + if (asID) + OPENSSL_free(asID); + CMS_ContentInfo_free(cms); + return r; +} + + + +const static struct option longopts[] = { + { "brief", no_argument, NULL, 'b' }, + { "print-cms", no_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { "signingtime", no_argument, NULL, 's' }, + { NULL } +}; + +static int usage (const char *jane, const int code) +{ + FILE *out = code ? stderr : stdout; + int i; + + fprintf(out, "usage: %s [options] ROA [ROA...]\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; +} + +/* + * Main program. + */ +int main (int argc, char *argv[]) +{ + int result = 0, print_brief = 0, print_signingtime = 0, print_cms = 0, c; + const char *jane = argv[0]; + ROA *r; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + while ((c = getopt_long(argc, argv, "bchs", longopts, NULL)) != -1) { + switch (c) { + case 'b': + print_brief = 1; + break; + case 'c': + print_cms = 1; + break; + case 's': + print_signingtime = 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) { + r = read_roa(*argv++, print_cms, 1, !print_brief, print_brief, print_signingtime); + result |= r == NULL; + ROA_free(r); + } + return result; +} diff --git a/rp/utils/print_roa/Makefile.in b/rp/utils/print_roa/Makefile.in new file mode 100644 index 00000000..5999b351 --- /dev/null +++ b/rp/utils/print_roa/Makefile.in @@ -0,0 +1,52 @@ +# $Id$ + +NAME = print_roa + +BIN = ${NAME} +SRC = ${NAME}.c +OBJ = ${NAME}.o + +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +INSTALL = @INSTALL@ -m 555 + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localstatedir = @localstatedir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +libdir = @libdir@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all: ${BIN} + +clean: + rm -rf ${BIN} ${OBJ} ${BIN}.dSYM + +${BIN}: ${SRC} + ${CC} ${CFLAGS} -o $@ ${SRC} ${LDFLAGS} ${LIBS} + +ROA_DIR = ${abs_top_builddir}/rpkid/tests/smoketest.dir/publication + +test: all + -date -u +'now: %Y%m%d%H%M%SZ' + if test -d ${ROA_DIR}; then find ${ROA_DIR} -type f -name '*.roa' -print -exec ./${BIN} {} \; ; else :; fi + +install: all + if test -d ${DESTDIR}${bindir} ; then :; else ${INSTALL} -d ${DESTDIR}${bindir}; fi + ${INSTALL} ${BIN} ${DESTDIR}${bindir} + +deinstall uninstall: + rm -f ${DESTDIR}${bindir}/${BIN} + +distclean: clean + rm -f Makefile diff --git a/rp/utils/print_rpki_manifest.c b/rp/utils/print_rpki_manifest.c new file mode 100644 index 00000000..f55f9916 --- /dev/null +++ b/rp/utils/print_rpki_manifest.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2014 Dragon Research Labs ("DRL") + * Portions copyright (C) 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$ */ + +/* + * Decoder test for RPKI manifests. + * + * NB: This does -not- check the CMS signatures, just the encoding. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <getopt.h> + +#include <openssl/bn.h> +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/safestack.h> +#include <openssl/conf.h> +#include <openssl/rand.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/cms.h> + +#include <rpki/manifest.h> + +/* + * Read manifest (CMS object) in DER format. + * + * NB: When invoked this way, CMS_verify() does -not- verify, it just decodes the ASN.1. + * + * OK, this does more than just reading the CMS. Refactor or rename, someday. + */ + +static const Manifest *read_manifest(const char *filename, + const int print_cms, + const int print_manifest, + const int print_signerinfo) +{ + CMS_ContentInfo *cms = NULL; + const ASN1_OBJECT *oid = NULL; + const Manifest *m = NULL; + char *mftnum = NULL; + BIGNUM *bn = NULL; + char buf[512]; + BIO *b = NULL; + int i, j; + + if ((b = BIO_new_file(filename, "r")) == NULL || + (cms = d2i_CMS_bio(b, NULL)) == NULL) + goto done; + BIO_free(b); + b = NULL; + + if (print_signerinfo) { + STACK_OF(CMS_SignerInfo) *signerInfos = CMS_get0_SignerInfos(cms); + STACK_OF(X509) *certs = CMS_get1_certs(cms); + STACK_OF(X509_CRL) *crls = CMS_get1_crls(cms); + printf("Certificates: %d\n", certs ? sk_X509_num(certs) : 0); + printf("CRLs: %d\n", crls ? sk_X509_CRL_num(crls) : 0); + for (i = 0; i < sk_CMS_SignerInfo_num(signerInfos); i++) { + CMS_SignerInfo *si = sk_CMS_SignerInfo_value(signerInfos, i); + ASN1_OCTET_STRING *hash = NULL; + printf("SignerId[%d]: ", i); + if (CMS_SignerInfo_get0_signer_id(si, &hash, NULL, NULL) && hash != NULL) + for (j = 0; j < hash->length; j++) + printf("%02x%s", hash->data[j], j == hash->length - 1 ? "" : ":"); + else + printf("[Could not read SID]"); + if (certs) + for (j = 0; j < sk_X509_num(certs); j++) + if (!CMS_SignerInfo_cert_cmp(si, sk_X509_value(certs, j))) + printf(" [Matches certificate %d]", j); + if ((j = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)) >= 0) { + X509_ATTRIBUTE *xa = CMS_signed_get_attr(si, j); + if (xa && !xa->single && sk_ASN1_TYPE_num(xa->value.set) == 1) { + ASN1_TYPE *so = sk_ASN1_TYPE_value(xa->value.set, 0); + switch (so->type) { + case V_ASN1_UTCTIME: + printf(" [signingTime(U) %s%s]", + so->value.utctime->data[0] < '5' ? "20" : "19", + so->value.utctime->data); + break; + case V_ASN1_GENERALIZEDTIME: + printf(" [signingTime(G) %s]", + so->value.generalizedtime->data); + break; + } + } + } + printf("\n"); + } + sk_X509_pop_free(certs, X509_free); + sk_X509_CRL_pop_free(crls, X509_CRL_free); + } + + if ((b = BIO_new(BIO_s_mem())) == NULL || + CMS_verify(cms, NULL, NULL, NULL, b, CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY) <= 0 || + (m = ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), b, NULL)) == NULL) + goto done; + BIO_free(b); + b = NULL; + + if (print_manifest) { + + if ((oid = CMS_get0_eContentType(cms)) == NULL) + goto done; + OBJ_obj2txt(buf, sizeof(buf), oid, 0); + printf("eContentType: %s\n", buf); + + if (m->version) + printf("version: %ld\n", ASN1_INTEGER_get(m->version)); + else + printf("version: 0 [Defaulted]\n"); + + bn = ASN1_INTEGER_to_BN(m->manifestNumber, NULL); + mftnum = BN_bn2dec(bn); + printf("manifestNumber: %s\n", mftnum); + + printf("thisUpdate: %s\n", m->thisUpdate->data); + printf("nextUpdate: %s\n", m->nextUpdate->data); + OBJ_obj2txt(buf, sizeof(buf), m->fileHashAlg, 0); + printf("fileHashAlg: %s\n", buf); + + for (i = 0; i < sk_FileAndHash_num(m->fileList); i++) { + FileAndHash *fah = sk_FileAndHash_value(m->fileList, i); + printf("fileList[%3d]: ", i); + for (j = 0; j < fah->hash->length; j++) + printf("%02x%s", fah->hash->data[j], j == fah->hash->length - 1 ? " " : ":"); + printf(" %s\n", fah->file->data); + } + + if (X509_cmp_current_time(m->nextUpdate) < 0) + printf("MANIFEST IS STALE\n"); + } + + if (print_cms) { + if (print_manifest) + printf("\n"); + fflush(stdout); + if ((b = BIO_new(BIO_s_fd())) == NULL) + goto done; + BIO_set_fd(b, 1, BIO_NOCLOSE); + CMS_ContentInfo_print_ctx(b, cms, 0, NULL); + BIO_free(b); + b = NULL; + } + + done: + if (ERR_peek_error()) + ERR_print_errors_fp(stderr); + BIO_free(b); + BN_free(bn); + if (mftnum) + OPENSSL_free(mftnum); + CMS_ContentInfo_free(cms); + return m; +} + + + +const static struct option longopts[] = { + { "print-cms", no_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { NULL } +}; + +static int usage (const char *jane, const int code) +{ + FILE *out = code ? stderr : stdout; + int i; + + fprintf(out, "usage: %s [options] manifest [manifest...]\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; +} + +/* + * Main program. + */ +int main (int argc, char *argv[]) +{ + int result = 0, print_cms = 0, c; + const char *jane = argv[0]; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + while ((c = getopt_long(argc, argv, "ch", longopts, NULL)) != -1) { + switch (c) { + case 'c': + print_cms = 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) + result |= read_manifest(*argv++, print_cms, 1, 1) == NULL; + return result; +} diff --git a/rp/utils/print_rpki_manifest/Makefile.in b/rp/utils/print_rpki_manifest/Makefile.in new file mode 100644 index 00000000..22f1b16b --- /dev/null +++ b/rp/utils/print_rpki_manifest/Makefile.in @@ -0,0 +1,52 @@ +# $Id$ + +NAME = print_rpki_manifest + +BIN = ${NAME} +SRC = ${NAME}.c +OBJ = ${NAME}.o + +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +INSTALL = @INSTALL@ -m 555 + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localstatedir = @localstatedir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +libdir = @libdir@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all: ${BIN} + +clean: + rm -rf ${BIN} ${OBJ} ${BIN}.dSYM + +${BIN}: ${SRC} + ${CC} ${CFLAGS} -o $@ ${SRC} ${LDFLAGS} ${LIBS} + +MANIFEST_DIR = ${abs_top_builddir}/rpkid/tests/smoketest.dir/publication + +test: all + -date -u +'now: %Y%m%d%H%M%SZ' + if test -d ${MANIFEST_DIR}; then find ${MANIFEST_DIR} -type f -name '*.mnf' -print -exec ./${BIN} {} \; ; else :; fi + +install: all + if test -d ${DESTDIR}${bindir} ; then :; else ${INSTALL} -d ${DESTDIR}${bindir}; fi + ${INSTALL} ${BIN} ${DESTDIR}${bindir} + +deinstall uninstall: + rm -f ${DESTDIR}${bindir}/${BIN} + +distclean: clean + rm -f Makefile diff --git a/rp/utils/scan_roas.c b/rp/utils/scan_roas.c new file mode 100644 index 00000000..f32e3827 --- /dev/null +++ b/rp/utils/scan_roas.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2014 Dragon Research Labs ("DRL") + * Portions copyright (C) 2011 Internet Systems Consortium ("ISC") + * + * 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 ISC DISCLAIM ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL OR + * ISC 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$ */ + +/* + * Decoder test for ROAs. + * + * NB: This does -not- check the CMS signatures, just the encoding. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> + +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/safestack.h> +#include <openssl/conf.h> +#include <openssl/rand.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/cms.h> + +#include <rpki/roa.h> + +/* + * How much buffer space do we need for a raw address? + */ +#define ADDR_RAW_BUF_LEN 16 + +/* + * How long can a filesystem path be? + */ +#define PATH_MAX 2048 + + + +/* + * Extract signing time from CMS message. + */ + +static char * +extract_signingTime(CMS_ContentInfo *cms, char *buffer, size_t buflen) +{ + STACK_OF(CMS_SignerInfo) *sis = NULL; + CMS_SignerInfo *si = NULL; + X509_ATTRIBUTE *xa = NULL; + ASN1_TYPE *so = NULL; + int i = -1; + + if (cms == NULL || + buffer == NULL || + buflen < sizeof("20010401123456Z") || + (sis = CMS_get0_SignerInfos(cms)) == NULL || + sk_CMS_SignerInfo_num(sis) != 1 || + (si = sk_CMS_SignerInfo_value(sis, 0)) < 0 || + (i = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)) < 0 || + (xa = CMS_signed_get_attr(si, i)) == NULL || + xa->single || + sk_ASN1_TYPE_num(xa->value.set) != 1 || + (so = sk_ASN1_TYPE_value(xa->value.set, 0)) == NULL) + return NULL; + + assert(buflen > 2); + buffer[buflen - 1] = '\0'; + + switch (so->type) { + case V_ASN1_UTCTIME: + strcpy(buffer, (so->value.utctime->data[0] >= '5') ? "19" : "20"); + return strncpy(buffer + 2, (const char *) so->value.utctime->data, buflen - 3); + case V_ASN1_GENERALIZEDTIME: + return strncpy(buffer, (const char *) so->value.generalizedtime->data, buflen - 1); + default: + return NULL; + } +} + + + +/* + * Expand the bitstring form of an address into a raw byte array. + * At the moment this is coded for simplicity, not speed. + */ +static void addr_expand(unsigned char *addr, + const ASN1_BIT_STRING *bs, + const int length) +{ + assert(bs->length >= 0 && bs->length <= length); + if (bs->length > 0) { + memcpy(addr, bs->data, bs->length); + if ((bs->flags & 7) != 0) { + unsigned char mask = 0xFF >> (8 - (bs->flags & 7)); + addr[bs->length - 1] &= ~mask; + } + } + memset(addr + bs->length, 0, length - bs->length); +} + +/* + * Extract the prefix length from a bitstring. + */ +#define addr_prefixlen(bs) ((int) ((bs)->length * 8 - ((bs)->flags & 7))) + +/* + * Read ROA (CMS object) in DER format. + * + * NB: When invoked this way, CMS_verify() does -not- verify, it just decodes the ASN.1. + */ +static int read_roa(const char *filename) +{ + char buffer[sizeof("20010401123456Z")], *b; + unsigned char addr[ADDR_RAW_BUF_LEN]; + CMS_ContentInfo *cms = NULL; + const ASN1_OBJECT *oid = NULL; + ROA *r = NULL; + char buf[512]; + BIO *bio; + int i, j, k, n, ok; + + if ((bio = BIO_new_file(filename, "r")) == NULL || + (cms = d2i_CMS_bio(bio, NULL)) == NULL) + goto done; + BIO_free(bio); + + if ((bio = BIO_new(BIO_s_mem())) == NULL || + CMS_verify(cms, NULL, NULL, NULL, bio, CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY) <= 0 || + (r = ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, NULL)) == NULL) + goto done; + + if (!extract_signingTime(cms, buffer, sizeof(buffer))) + goto done; + printf("%s ", buffer); + + printf("%ld", ASN1_INTEGER_get(r->asID)); + + for (i = 0; i < sk_ROAIPAddressFamily_num(r->ipAddrBlocks); i++) { + + ROAIPAddressFamily *f = sk_ROAIPAddressFamily_value(r->ipAddrBlocks, i); + + unsigned afi = (f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]); + + for (j = 0; j < sk_ROAIPAddress_num(f->addresses); j++) { + ROAIPAddress *a = sk_ROAIPAddress_value(f->addresses, j); + + printf(" "); + + switch (afi) { + + case IANA_AFI_IPV4: + addr_expand(addr, a->IPAddress, 4); + printf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + break; + + case IANA_AFI_IPV6: + addr_expand(addr, a->IPAddress, 16); + for (n = 16; n > 1 && addr[n-1] == 0x00 && addr[n-2] == 0x00; n -= 2) + ; + for (k = 0; k < n; k += 2) + printf("%x%s", (addr[k] << 8) | addr[k+1], (k < 14 ? ":" : "")); + if (k < 16) + printf(":"); + if (k == 0) + printf(":"); + break; + + default: + break; + } + + printf("/%u", addr_prefixlen(a->IPAddress)); + + if (a->maxLength) + printf("-%ld", ASN1_INTEGER_get(a->maxLength)); + } + } + printf("\n"); + + done: + ok = r != NULL; + + if (ERR_peek_error()) + ERR_print_errors_fp(stderr); + BIO_free(bio); + CMS_ContentInfo_free(cms); + ROA_free(r); + + return ok; +} + + + +/** + * Check str for a trailing suffix. + */ +static int endswith(const char *str, const char *suffix) +{ + size_t len_str, len_suffix; + assert(str != NULL && suffix != NULL); + len_str = strlen(str); + len_suffix = strlen(suffix); + return len_str >= len_suffix && !strcmp(str + len_str - len_suffix, suffix); +} + + + +/** + * Walk directory tree, looking for ROAs. + */ +static int walk(const char *name) +{ + int need_slash, ok = 1; + char path[PATH_MAX]; + struct dirent *d; + size_t len; + DIR *dir; + + assert(name); + len = strlen(name); + + assert(len > 0 && len < sizeof(path)); + need_slash = name[len - 1] != '/'; + + if ((dir = opendir(name)) == NULL) + return 0; + + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || + !strcmp(d->d_name, "..")) + continue; + if (len + strlen(d->d_name) + need_slash >= sizeof(path)) { + ok = 0; + goto done; + } + strcpy(path, name); + if (need_slash) + strcat(path, "/"); + strcat(path, d->d_name); + switch (d->d_type) { + case DT_DIR: + ok &= walk(path); + continue; + default: + if (endswith(path, ".roa")) + ok &= read_roa(path); + continue; + } + } + + done: + closedir(dir); + return ok; +} + + + +static void usage (const char *jane, const int code) +{ + fprintf(code ? stderr : stdout, "usage: %s authtree [authtree...]\n", jane); + exit(code); +} + +/* + * Main program. + */ +int main (int argc, char *argv[]) +{ + int i, ok = 1; + + if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) + usage(argv[0], 0); + + if (argc < 2) + usage(argv[0], 1); + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + for (i = 1; i < argc; i++) + ok &= walk(argv[i]); + + return !ok; +} diff --git a/rp/utils/scan_roas/Makefile.in b/rp/utils/scan_roas/Makefile.in new file mode 100644 index 00000000..7707969c --- /dev/null +++ b/rp/utils/scan_roas/Makefile.in @@ -0,0 +1,52 @@ +# $Id$ + +NAME = scan_roas + +BIN = ${NAME} +SRC = ${NAME}.c +OBJ = ${NAME}.o + +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +INSTALL = @INSTALL@ -m 555 + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localstatedir = @localstatedir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +libdir = @libdir@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all: ${BIN} + +clean: + rm -rf ${BIN} ${OBJ} ${BIN}.dSYM + +${BIN}: ${SRC} + ${CC} ${CFLAGS} -o $@ ${SRC} ${LDFLAGS} ${LIBS} + +ROA_DIR = ${abs_top_builddir}/rpkid/tests/smoketest.dir/publication + +test: all + -date -u +'now: %Y%m%d%H%M%SZ' + if test -d ${ROA_DIR}; then ./${BIN} ${ROA_DIR} ; else :; fi + +install: all + if test -d ${DESTDIR}${bindir} ; then :; else ${INSTALL} -d ${DESTDIR}${bindir}; fi + ${INSTALL} ${BIN} ${DESTDIR}${bindir} + +deinstall uninstall: + rm -f ${DESTDIR}${bindir}/${BIN} + +distclean: clean + rm -f Makefile diff --git a/rp/utils/scan_routercerts.py b/rp/utils/scan_routercerts.py new file mode 100755 index 00000000..342fa272 --- /dev/null +++ b/rp/utils/scan_routercerts.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# $Id$ +# +# Copyright (C) 2014 Dragon Research Labs ("DRL") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND DRL DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL DRL 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. + +""" +Scan rcynic validated output looking for router certificates, print +out stuff that the rpki-rtr code cares about. +""" + +# This program represents a weird temporary state, mostly to avoid +# diving into a recursive yak shaving exercise. +# +# Under the old scheme, anything used by the RP code should be either +# C code or pure Python code using just the standard libraries. This +# has gotten silly, but we haven't yet refactored the current packaged +# builds from two packages into three (adding a -libs package). +# +# So, by rights, this program should be a C monstrosity written using +# the OpenSSL C API. I started coding it that way, but it was just +# too painful for something we're probably going to rewrite as a few +# lines of Python once we refactor, but by the same token I didn't +# want to delay router certificate support until the refactoring. +# +# So this program anticipates the new scheme of things, but makes one +# concession to current reality: if it has a problem importing the +# RPKI-specific libraries, it just quietly exits as if everything were +# fine and there simply are no router certificates to report. This +# isn't the right answer in the long run, but will suffice to avoid +# further bald yaks. + +import os +import sys +import base64 + +try: + import rpki.POW + import rpki.oids +except ImportError: + sys.exit(0) + +rcynic_dir = sys.argv[1] + +for root, dirs, files in os.walk(rcynic_dir): + for fn in files: + if not fn.endswith(".cer"): + continue + x = rpki.POW.X509.derReadFile(os.path.join(root, fn)) + + if rpki.oids.id_kp_bgpsec_router not in (x.getEKU() or ()): + continue + + sys.stdout.write(base64.urlsafe_b64encode(x.getSKI()).rstrip("=")) + for min_asn, max_asn in x.getRFC3779()[0]: + for asn in xrange(min_asn, max_asn + 1): + sys.stdout.write(" %s" % asn) + sys.stdout.write(" %s\n" % base64.b64encode(x.getPublicKey().derWritePublic())) diff --git a/rp/utils/scan_routercerts/Makefile.in b/rp/utils/scan_routercerts/Makefile.in new file mode 100644 index 00000000..715d1325 --- /dev/null +++ b/rp/utils/scan_routercerts/Makefile.in @@ -0,0 +1,41 @@ +# $Id$ + +NAME = scan_routercerts + +BIN = ${NAME} + +INSTALL = @INSTALL@ -m 555 + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localstatedir = @localstatedir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +libdir = @libdir@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all clean: + @true + +ROUTERCERT_DIR = ${abs_top_builddir}/rpkid/tests/smoketest.dir/publication + +test: all + -date -u +'now: %Y%m%d%H%M%SZ' + if test -d ${ROUTERCERT_DIR}; then ./${BIN} ; else :; fi + +install: all + if test -d ${DESTDIR}${bindir} ; then :; else ${INSTALL} -d ${DESTDIR}${bindir}; fi + ${INSTALL} ${BIN} ${DESTDIR}${bindir} + +deinstall uninstall: + rm -f ${DESTDIR}${bindir}/${BIN} + +distclean: clean + rm -f Makefile diff --git a/rp/utils/strip_roa.sh b/rp/utils/strip_roa.sh new file mode 100755 index 00000000..e2dacf86 --- /dev/null +++ b/rp/utils/strip_roa.sh @@ -0,0 +1,39 @@ +#!/bin/sh - +# $Id$ +# +# Copyright (C) 2010 Internet Systems Consortium ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. +# +# Strip boring parts of print_roa's output to make a (somewhat) terser +# description, one line per ROA. This is intended for use in +# comparing sets of ROAs using text comparision tools like "diff" or +# "comm". One could definitely do something prettier, but this +# suffices for basic tests. +# +# Use this as in a shell pipeline to postprocess print_roa's output. + +awk ' + /Certificate/ { + roa[++n] = ""; + } + /asID|addressFamily|IPaddress/ { + roa[n] = roa[n] " " $0; + } + END { + for (i in roa) + print roa[i]; + } +' | +tr -s \\011 \\040 | +sort -u diff --git a/rp/utils/table.awk b/rp/utils/table.awk new file mode 100644 index 00000000..d8627f67 --- /dev/null +++ b/rp/utils/table.awk @@ -0,0 +1,35 @@ +#!/usr/bin/awk -f +# $Id$ +# +# Reformat uri.c's output in a way that's more useful +# for some kinds of scripting. Perhaps this functionality should be +# part of uri.c itself, but for now this script will do. + +BEGIN { + cmd = "find /var/rcynic/data/unauthenticated -type f -name '*.cer' -print0 | xargs -0 ./uri -d"; + while ((cmd | getline) == 1) { + if ($1 == "File") { + if (f) + print f, u, a, s, c; + a = s = c = "-"; + f = $2; + sub("/var/rcynic/data/unauthenticated/","rsync://"); + u = $2; + continue; + } + if ($1 == "SIA:") { + s = $2; + continue; + } + if ($1 == "AIA:") { + a = $2; + continue; + } + if ($1 == "CRL:") { + c = $2; + continue; + } + } + if (f != "-") + print f, u, a, s, c; +} diff --git a/rp/utils/test_roa.sh b/rp/utils/test_roa.sh new file mode 100644 index 00000000..43d20898 --- /dev/null +++ b/rp/utils/test_roa.sh @@ -0,0 +1,35 @@ +#!/bin/sh - +# +# Copyright (C) 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 notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL 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$ + +auth_dir="${1?"usage: $0 authenticated_certificate_tree prefix [prefix...]"}" + +rm -rf hashed-pem-dir +mkdir hashed-pem-dir + +../hashdir/hashdir "$auth_dir" hashed-pem-dir >/dev/null + +./find_roa "$@" | awk ' + $1 == "ASN" && $3 == "prefix" && $5 == "ROA" { + print ""; + print "Found match:" + print; + print "Verifying certificate chain and signatures:" + roa = $6; + if (!system("../../openssl/openssl/apps/openssl cms -verify -inform DER -out /dev/null -CApath hashed-pem-dir -in " roa)) + system("../print_roa/print_roa " roa); + }' diff --git a/rp/utils/uri.c b/rp/utils/uri.c new file mode 100644 index 00000000..6353e8e5 --- /dev/null +++ b/rp/utils/uri.c @@ -0,0 +1,248 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> + +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/cms.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/safestack.h> + +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; +} diff --git a/rp/utils/uri/Makefile.in b/rp/utils/uri/Makefile.in new file mode 100644 index 00000000..fc545060 --- /dev/null +++ b/rp/utils/uri/Makefile.in @@ -0,0 +1,31 @@ +# $Id$ + +NAME = uri + +BIN = ${NAME} +SRC = ${NAME}.c +OBJ = ${NAME}.o + +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +all: ${BIN} + +clean: + rm -rf ${BIN} ${OBJ} ${BIN}.dSYM + +${BIN}: ${SRC} + ${CC} ${CFLAGS} -o $@ ${SRC} ${LDFLAGS} ${LIBS} + +test: + @true + +install deinstall uninstall: + @true + +distclean: clean + rm -f Makefile |