aboutsummaryrefslogtreecommitdiff
path: root/rp/utils
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2014-04-05 22:42:12 +0000
committerRob Austein <sra@hactrn.net>2014-04-05 22:42:12 +0000
commitfe0bf509f528dbdc50c7182f81057c6a4e15e4bd (patch)
tree07c9a923d4a0ccdfea11c49cd284f6d5757c5eda /rp/utils
parentaa28ef54c271fbe4d52860ff8cf13cab19e2207c (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.in9
-rw-r--r--rp/utils/README12
-rw-r--r--rp/utils/dot.awk34
-rw-r--r--rp/utils/find_roa.c356
-rw-r--r--rp/utils/find_roa/Makefile.in56
-rw-r--r--rp/utils/hashdir.c217
-rw-r--r--rp/utils/hashdir/Makefile.in55
-rw-r--r--rp/utils/print_roa.c384
-rw-r--r--rp/utils/print_roa/Makefile.in52
-rw-r--r--rp/utils/print_rpki_manifest.c235
-rw-r--r--rp/utils/print_rpki_manifest/Makefile.in52
-rw-r--r--rp/utils/scan_roas.c305
-rw-r--r--rp/utils/scan_roas/Makefile.in52
-rwxr-xr-xrp/utils/scan_routercerts.py69
-rw-r--r--rp/utils/scan_routercerts/Makefile.in41
-rwxr-xr-xrp/utils/strip_roa.sh39
-rw-r--r--rp/utils/table.awk35
-rw-r--r--rp/utils/test_roa.sh35
-rw-r--r--rp/utils/uri.c248
-rw-r--r--rp/utils/uri/Makefile.in31
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