Include SLURM functions and some adequations to run the validator.
-AM_CFLAGS = -pedantic -Wall -std=gnu11 -O3
-AM_LDFLAGS =
-
-bin_PROGRAMS = rtr_server
-
-rtr_server_SOURCES = main.c
-rtr_server_SOURCES += address.c address.h
-rtr_server_SOURCES += array_list.h
-rtr_server_SOURCES += clients.c clients.h
-rtr_server_SOURCES += common.c common.h
-rtr_server_SOURCES += configuration.c configuration.h
-rtr_server_SOURCES += csv.c csv.h
-rtr_server_SOURCES += json_parser.c json_parser.h
-rtr_server_SOURCES += line_file.c line_file.h
-rtr_server_SOURCES += notify.c notify.h
-rtr_server_SOURCES += slurm_db.c slurm_db.h
-rtr_server_SOURCES += slurm_loader.c slurm_loader.h
-rtr_server_SOURCES += slurm_parser.c slurm_parser.h
-rtr_server_SOURCES += updates_daemon.c updates_daemon.h
-rtr_server_SOURCES += vrps.c vrps.h
-
-rtr_server_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
-rtr_server_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
-rtr_server_SOURCES += rtr/pdu_sender.c rtr/pdu_sender.h
-rtr_server_SOURCES += rtr/pdu_serializer.c rtr/pdu_serializer.h
-rtr_server_SOURCES += rtr/pdu.c rtr/pdu.h
-rtr_server_SOURCES += rtr/primitive_reader.c rtr/primitive_reader.h
-rtr_server_SOURCES += rtr/primitive_writer.c rtr/primitive_writer.h
-rtr_server_SOURCES += rtr/rtr.c rtr/rtr.h
-
-rtr_server_SOURCES += crypto/base64.c crypto/base64.h
-
-rtr_server_LDADD = ${JANSSON_LIBS}
+# Comment these out during releases.
+# Also increase optimization I guess (-O0 -> -On)
+#CFLAGS_DEBUG = -DDEBUG
+LDFLAGS_DEBUG = -rdynamic
+
+bin_PROGRAMS = fort
+
+fort_SOURCES = main.c
+
+fort_SOURCES += address.h address.c
+fort_SOURCES += algorithm.h algorithm.c
+fort_SOURCES += certificate_refs.h certificate_refs.c
+fort_SOURCES += console_handler.h console_handler.c
+fort_SOURCES += common.h
+fort_SOURCES += config.h config.c
+fort_SOURCES += debug.h debug.c
+fort_SOURCES += extension.h extension.c
+fort_SOURCES += file.h file.c
+fort_SOURCES += line_file.h line_file.c
+fort_SOURCES += log.h log.c
+fort_SOURCES += nid.h nid.c
+fort_SOURCES += random.h random.c
+fort_SOURCES += resource.h resource.c
+fort_SOURCES += rpp.h rpp.c
+fort_SOURCES += sorted_array.h sorted_array.c
+fort_SOURCES += state.h state.c
+fort_SOURCES += str.h str.c
+fort_SOURCES += thread_var.h thread_var.c
+fort_SOURCES += uri.h uri.c
+fort_SOURCES += json_handler.h json_handler.c
+fort_SOURCES += validation_handler.h validation_handler.c
+
+fort_SOURCES += rsync/rsync.h rsync/rsync.c
+
+fort_SOURCES += asn1/content_info.h asn1/content_info.c
+fort_SOURCES += asn1/decode.h asn1/decode.c
+fort_SOURCES += asn1/oid.h asn1/oid.c
+fort_SOURCES += asn1/signed_data.h asn1/signed_data.c
+
+fort_SOURCES += config/boolean.c config/boolean.h
+fort_SOURCES += config/filename_format.h config/filename_format.c
+fort_SOURCES += config/str.c config/str.h
+fort_SOURCES += config/string_array.h config/string_array.c
+fort_SOURCES += config/sync_strategy.h config/sync_strategy.c
+fort_SOURCES += config/types.h
+fort_SOURCES += config/uint.c config/uint.h
+fort_SOURCES += config/uint32.c config/uint32.h
+
+fort_SOURCES += data_structure/array_list.h
+
+fort_SOURCES += crypto/base64.h crypto/base64.c
+fort_SOURCES += crypto/hash.h crypto/hash.c
+
+fort_SOURCES += data_structure/circular_indexer.h
+fort_SOURCES += data_structure/circular_indexer.c
+
+fort_SOURCES += object/certificate.h object/certificate.c
+fort_SOURCES += object/crl.h object/crl.c
+fort_SOURCES += object/ghostbusters.h object/ghostbusters.c
+fort_SOURCES += object/manifest.h object/manifest.c
+fort_SOURCES += object/name.h object/name.c
+fort_SOURCES += object/roa.h object/roa.c
+fort_SOURCES += object/signed_object.h object/signed_object.c
+fort_SOURCES += object/tal.h object/tal.c
+fort_SOURCES += object/vcard.h object/vcard.c
+
+fort_SOURCES += resource/ip4.h resource/ip4.c
+fort_SOURCES += resource/ip6.h resource/ip6.c
+fort_SOURCES += resource/asn.h resource/asn.c
+
+fort_SOURCES += array_list.h
+fort_SOURCES += clients.c clients.h
+fort_SOURCES += common.c common.h
++fort_SOURCES += json_parser.c json_parser.h
+fort_SOURCES += notify.c notify.h
++fort_SOURCES += slurm_db.c slurm_db.h
++fort_SOURCES += slurm_loader.c slurm_loader.h
++fort_SOURCES += slurm_parser.c slurm_parser.h
+fort_SOURCES += updates_daemon.c updates_daemon.h
+
+fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
+fort_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
+fort_SOURCES += rtr/pdu_sender.c rtr/pdu_sender.h
+fort_SOURCES += rtr/pdu_serializer.c rtr/pdu_serializer.h
+fort_SOURCES += rtr/pdu.c rtr/pdu.h
+fort_SOURCES += rtr/primitive_reader.c rtr/primitive_reader.h
+fort_SOURCES += rtr/primitive_writer.c rtr/primitive_writer.h
+fort_SOURCES += rtr/rtr.c rtr/rtr.h
+
+fort_SOURCES += rtr/db/delta.c rtr/db/delta.h
+fort_SOURCES += rtr/db/roa_tree.c rtr/db/roa_tree.h
+fort_SOURCES += rtr/db/roa.c rtr/db/roa.h
+fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h
+
+fort_CFLAGS = -Wall
+# Feel free to temporarily remove this one if you're not using gcc 7.3.0.
+#fort_CFLAGS += $(GCC_WARNS)
+fort_CFLAGS += -std=gnu99 -O0 -g $(CFLAGS_DEBUG)
+fort_LDFLAGS = $(LDFLAGS_DEBUG)
+fort_LDADD = ${JANSSON_LIBS}
# I'm tired of scrolling up, but feel free to comment this out.
GCC_WARNS = -fmax-errors=1
return 0;
}
+/**
+ * Translates an `IPAddress_t` to its equivalent `struct ipv6_prefix`.
+ */
int
-prefix6_decode(const char *str, struct ipv6_prefix *result)
+prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
{
- int error;
+ struct in6_addr suffix;
+ int len;
- if (str == NULL) {
- warnx("Null string received, can't decode IPv6 prefix");
- return -EINVAL;
+ if (str->size > 16) {
+ return pr_err("IPv6 address has too many octets. (%zu)",
+ str->size);
+ }
+ if (str->bits_unused < 0 || 7 < str->bits_unused) {
+ return pr_err("Bit string IPv6 address's unused bits count (%d) is out of range (0-7).",
+ str->bits_unused);
}
- error = str2addr6(str, &result->addr);
- if (error) {
- warnx("Invalid IPv6 prefix '%s'", str);
+ memset(&result->addr, 0, sizeof(result->addr));
+ memcpy(&result->addr, str->buf, str->size);
+ len = 8 * str->size - str->bits_unused;
+
+ if (len < 0 || 128 < len) {
+ return pr_err("IPv6 prefix length (%d) is out of bounds (0-128).",
+ len);
+ }
+
+ result->len = len;
+
+ memset(&suffix, 0, sizeof(suffix));
+ ipv6_suffix_mask(result->len, &suffix);
+ if (addr6_bitwise_and(&result->addr, &suffix)) {
+ return pr_err("IPv6 prefix '%s/%u' has enabled suffix bits.",
+ v6addr2str(&result->addr), result->len);
+ }
+
+ return 0;
+}
+
+static int
+check_order4(struct ipv4_range *result)
+{
+ if (ntohl(result->min.s_addr) > ntohl(result->max.s_addr)) {
+ return pr_err("The IPv4 range '%s-%s' is inverted.",
+ v4addr2str(&result->min), v4addr2str2(&result->max));
+ }
+
+ return 0;
+}
+
+/**
+ * If @range could have been encoded as a prefix, this function errors.
+ *
+ * rfc3779#section-2.2.3.7
+ */
+static int
+check_encoding4(struct ipv4_range *range)
+{
+ const uint32_t MIN = ntohl(range->min.s_addr);
+ const uint32_t MAX = ntohl(range->max.s_addr);
+ uint32_t mask;
+
+ for (mask = 0x80000000u; mask != 0; mask >>= 1)
+ if ((MIN & mask) != (MAX & mask))
+ break;
+
+ for (; mask != 0; mask >>= 1)
+ if (((MIN & mask) != 0) || ((MAX & mask) == 0))
+ return 0;
+
+ return pr_err("IPAddressRange '%s-%s' is a range, but should have been encoded as a prefix.",
+ v4addr2str(&range->min), v4addr2str2(&range->max));
+}
+
+/**
+ * Translates an `IPAddressRange_t` to its equivalent `struct ipv4_range`.
+ */
+int
+range4_decode(IPAddressRange_t const *input, struct ipv4_range *result)
+{
+ struct ipv4_prefix prefix;
+ int error;
+
+ error = prefix4_decode(&input->min, &prefix);
+ if (error)
+ return error;
+ result->min = prefix.addr;
+
+ error = prefix4_decode(&input->max, &prefix);
+ if (error)
+ return error;
+ result->max.s_addr = prefix.addr.s_addr | be32_suffix_mask(prefix.len);
+
+ error = check_order4(result);
+ if (error)
return error;
+
+ return check_encoding4(result);
+}
+
+static int
+check_order6(struct ipv6_range *result)
+{
+ uint32_t min;
+ uint32_t max;
+ unsigned int quadrant;
+
+ for (quadrant = 0; quadrant < 4; quadrant++) {
+ min = addr6_get_quadrant(&result->min, quadrant);
+ max = addr6_get_quadrant(&result->max, quadrant);
+ if (min > max) {
+ return pr_err("The IPv6 range '%s-%s' is inverted.",
+ v6addr2str(&result->min),
+ v6addr2str2(&result->max));
+ } else if (min < max) {
+ return 0; /* result->min < result->max */
+ }
+ }
+
+ return 0; /* result->min == result->max */
+}
+
+static int
+pr_bad_encoding(struct ipv6_range *range)
+{
+ return pr_err("IPAddressRange %s-%s is a range, but should have been encoded as a prefix.",
+ v6addr2str(&range->min), v6addr2str2(&range->max));
+}
+
+static int
+__check_encoding6(struct ipv6_range *range, unsigned int quadrant,
+ uint32_t mask)
+{
+ uint32_t min;
+ uint32_t max;
+
+ for (; quadrant < 4; quadrant++) {
+ min = addr6_get_quadrant(&range->min, quadrant);
+ max = addr6_get_quadrant(&range->max, quadrant);
+ for (; mask != 0; mask >>= 1)
+ if (((min & mask) != 0) || ((max & mask) == 0))
+ return 0;
+ mask = 0x80000000u;
+ }
+
+ return pr_bad_encoding(range);
+}
+
+static int
+check_encoding6(struct ipv6_range *range)
+{
+ uint32_t min;
+ uint32_t max;
+ unsigned int quadrant;
+ uint32_t mask;
+
+ for (quadrant = 0; quadrant < 4; quadrant++) {
+ min = addr6_get_quadrant(&range->min, quadrant);
+ max = addr6_get_quadrant(&range->max, quadrant);
+ for (mask = 0x80000000u; mask != 0; mask >>= 1)
+ if ((min & mask) != (max & mask))
+ return __check_encoding6(range, quadrant, mask);
}
- }
+ return pr_bad_encoding(range);
+}
+
+/**
+ * Translates an `IPAddressRange_t` to its equivalent `struct ipv6_range`.
+ */
+int
+range6_decode(IPAddressRange_t const *input, struct ipv6_range *result)
+{
+ struct ipv6_prefix prefix;
+ int error;
+
+ error = prefix6_decode(&input->min, &prefix);
+ if (error)
+ return error;
+ result->min = prefix.addr;
+
+ error = prefix6_decode(&input->max, &prefix);
+ if (error)
+ return error;
+ result->max = prefix.addr;
+ ipv6_suffix_mask(prefix.len, &result->max);
+
+ error = check_order6(result);
+ if (error)
+ return error;
+
+ return check_encoding6(result);
+}
++
++static char const *
++addr2str4(struct in_addr *addr, char *buffer)
++{
++ return inet_ntop(AF_INET, addr, buffer, INET_ADDRSTRLEN);
++}
++
++static char const *
++addr2str6(struct in6_addr *addr, char *buffer)
++{
++ return inet_ntop(AF_INET6, addr, buffer, INET6_ADDRSTRLEN);
++}
++
++static int const
++str2addr4(const char *addr, struct in_addr *dst)
++{
++ if (!inet_pton(AF_INET, addr, dst))
++ return -EINVAL;
+ return 0;
-prefix_length_decode (const char *text, unsigned int *dst, int max_value)
++}
++
++static int const
++str2addr6(const char *addr, struct in6_addr *dst)
++{
++ if (!inet_pton(AF_INET6, addr, dst))
++ return -EINVAL;
++ return 0;
++}
+
+ int
- if (text == NULL) {
- warnx("Null string received, can't decode prefix length");
- return -EINVAL;
- }
++str_to_prefix4(const char *str, struct ipv4_prefix *result)
++{
++ int error;
++
++ if (str == NULL)
++ return pr_err("Can't parse NULL IPv4 prefix");
++
++ error = str2addr4(str, &result->addr);
++ if (error)
++ return pr_err("Invalid IPv4 prefix '%s'", str);
++
++ return 0;
++}
++
++int
++str_to_prefix6(const char *str, struct ipv6_prefix *result)
++{
++ int error;
++
++ if (str == NULL)
++ return pr_err("Can't parse NULL IPv6 prefix");
++
++ error = str2addr6(str, &result->addr);
++ if (error)
++ return pr_err("Invalid IPv6 prefix '%s'", str);
++
++ return 0;
++}
++
++int
++str_to_prefix_length(const char *text, uint8_t *dst, uint8_t max_value)
+ {
+ unsigned long len;
+
- warn("Invalid prefix length '%s'", text);
++ if (text == NULL)
++ return pr_err("Can't decode NULL prefix length");
+
+ errno = 0;
+ len = strtoul(text, NULL, 10);
+ if (errno) {
- if (len < 0 || max_value < len) {
- warnx("Prefix length (%ld) is out of range (0-%d).",
++ pr_errno(errno, "Invalid prefix length '%s'", text);
+ return -EINVAL;
+ }
+ /* An underflow or overflow will be considered here */
- return -EINVAL;
- }
- *dst = (unsigned int) len;
++ if (len < 0 || max_value < len)
++ return pr_err("Prefix length (%ld) is out of range (0-%d).",
+ len, max_value);
-prefix4_validate (struct ipv4_prefix *prefix)
++
++ *dst = (uint8_t) len;
+ return 0;
+ }
+
+ int
- if ((prefix->addr.s_addr & be32_suffix_mask(prefix->len)) != 0) {
- warnx("IPv4 prefix %s/%u has enabled suffix bits.",
++ipv4_prefix_validate(struct ipv4_prefix *prefix)
+ {
+ char buffer[INET_ADDRSTRLEN];
+
- return -EINVAL;
- }
++ if ((prefix->addr.s_addr & be32_suffix_mask(prefix->len)) != 0)
++ return pr_err("IPv4 prefix %s/%u has enabled suffix bits.",
+ addr2str4(&prefix->addr, buffer), prefix->len);
-prefix6_validate (struct ipv6_prefix *prefix)
++
+ return 0;
+ }
+
+ int
- || (prefix->addr.s6_addr32[3] & suffix.s6_addr32[3])) {
- warnx("IPv6 prefix %s/%u has enabled suffix bits.",
++ipv6_prefix_validate(struct ipv6_prefix *prefix)
+ {
+ struct in6_addr suffix;
+ char buffer[INET6_ADDRSTRLEN];
+
+ memset(&suffix, 0, sizeof(suffix));
+ ipv6_suffix_mask(prefix->len, &suffix);
+ if ((prefix->addr.s6_addr32[0] & suffix.s6_addr32[0])
+ || (prefix->addr.s6_addr32[1] & suffix.s6_addr32[1])
+ || (prefix->addr.s6_addr32[2] & suffix.s6_addr32[2])
- return -EINVAL;
- }
++ || (prefix->addr.s6_addr32[3] & suffix.s6_addr32[3]))
++ return pr_err("IPv6 prefix %s/%u has enabled suffix bits.",
+ addr2str6(&prefix->addr, buffer), prefix->len);
++
+ return 0;
+ }
struct ipv6_prefix {
struct in6_addr addr;
- unsigned int len;
+ uint8_t len;
};
-int prefix4_decode(const char *, struct ipv4_prefix *);
-int prefix6_decode(const char *, struct ipv6_prefix *);
+struct ipv4_range {
+ struct in_addr min;
+ struct in_addr max;
+};
+
+struct ipv6_range {
+ struct in6_addr min;
+ struct in6_addr max;
+};
+
+uint32_t u32_suffix_mask(unsigned int);
+uint32_t be32_suffix_mask(unsigned int);
+void ipv6_suffix_mask(unsigned int, struct in6_addr *);
+
+bool prefix4_equals(struct ipv4_prefix const *, struct ipv4_prefix const *);
+bool prefix6_equals(struct ipv6_prefix const *, struct ipv6_prefix const *);
+
+int prefix4_decode(IPAddress_t const *, struct ipv4_prefix *);
+int prefix6_decode(IPAddress_t const *, struct ipv6_prefix *);
+int range4_decode(IPAddressRange_t const *, struct ipv4_range *);
+int range6_decode(IPAddressRange_t const *, struct ipv6_range *);
-int prefix_length_decode(const char *, unsigned int *, int);
++int str_to_prefix4(const char *, struct ipv4_prefix *);
++int str_to_prefix6(const char *, struct ipv6_prefix *);
++int str_to_prefix_length(const char *, uint8_t *, uint8_t);
+
-int prefix4_validate (struct ipv4_prefix *);
-int prefix6_validate (struct ipv6_prefix *);
++int ipv4_prefix_validate(struct ipv4_prefix *);
++int ipv6_prefix_validate(struct ipv6_prefix *);
+
#endif /* SRC_ADDRESS_H_ */
--- /dev/null
- },
+#include "config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include "common.h"
+#include "json_handler.h"
+#include "log.h"
+#include "config/boolean.h"
+#include "config/str.h"
+#include "config/uint.h"
+#include "config/uint32.h"
+
+/**
+ * To add a member to this structure,
+ *
+ * 1. Add it.
+ * 2. Add its metadata somewhere in @groups.
+ * 3. Add default value to set_default_values().
+ * 4. Create the getter.
+ *
+ * Assuming you don't need to create a data type, that should be all.
+ */
+struct rpki_config {
+ /** TAL file name or directory. TODO support directories */
+ char *tal;
+ /** Path of our local clone of the repository */
+ char *local_repository;
+ /** Synchronization (currently only RSYNC) download strategy. */
+ enum sync_strategy sync_strategy;
+ /**
+ * Handle TAL URIs in random order?
+ * (https://tools.ietf.org/html/rfc7730#section-3, last
+ * paragraphs)
+ */
+ bool shuffle_tal_uris;
+ /**
+ * rfc6487#section-7.2, last paragraph.
+ * Prevents arbitrarily long paths and loops.
+ */
+ unsigned int maximum_certificate_depth;
+
+ struct {
+ /** The listener address of the RTR server. */
+ char *address;
+ /** TODO document */
+ char *port;
+ /** Maximum accepted client connections */
+ unsigned int queue;
+
+ /** Interval used to look for updates at VRPs location */
+ /* TODO rename */
+ unsigned int vrps_check_interval;
+
+ /** Intervals use at RTR v1 End of data PDU **/
+ uint32_t refresh_interval;
+ uint32_t retry_interval;
+ uint32_t expire_interval;
++
++ /** Directory where the .slurm files are located */
++ char *slurm_location;
+ } server;
+
+ struct {
+ char *program;
+ struct {
+ struct string_array flat;
+ struct string_array recursive;
+ } args;
+ } rsync;
+
+ struct {
+ /** Print ANSI color codes? */
+ bool color;
+ /** Format in which file names will be printed. */
+ enum filename_format filename_format;
+ } log;
+};
+
+static void print_usage(FILE *, bool);
+
+#define DECLARE_HANDLE_FN(name) \
+ static int name( \
+ struct option_field const *, \
+ char * \
+ )
+DECLARE_HANDLE_FN(handle_help);
+DECLARE_HANDLE_FN(handle_usage);
+DECLARE_HANDLE_FN(handle_version);
+DECLARE_HANDLE_FN(handle_json);
+
+static char const *program_name;
+static struct rpki_config rpki_config;
+
+/**
+ * An option that takes no arguments, is not correlated to any rpki_config
+ * fields, and is entirely managed by its handler function.
+ */
+static const struct global_type gt_callback = {
+ .has_arg = no_argument,
+};
+
+static const struct option_field options[] = {
+
+ /* ARGP-only, non-fields */
+ {
+ .id = 'h',
+ .name = "help",
+ .type = >_callback,
+ .handler = handle_help,
+ .doc = "Give this help list",
+ .availability = AVAILABILITY_GETOPT,
+ }, {
+ .id = 1000,
+ .name = "usage",
+ .type = >_callback,
+ .handler = handle_usage,
+ .doc = "Give a short usage message",
+ .availability = AVAILABILITY_GETOPT,
+ }, {
+ .id = 'V',
+ .name = "version",
+ .type = >_callback,
+ .handler = handle_version,
+ .doc = "Print program version",
+ .availability = AVAILABILITY_GETOPT,
+ }, {
+ .id = 'f',
+ .name = "configuration-file",
+ .type = >_string,
+ .handler = handle_json,
+ .doc = "JSON file additional configuration will be read from",
+ .arg_doc = "<file>",
+ .availability = AVAILABILITY_GETOPT,
+ },
+
+ /* Root fields */
+ {
+ .id = 't',
+ .name = "tal",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, tal),
+ .doc = "Path to the TAL file",
+ .arg_doc = "<file>",
+ }, {
+ .id = 'r',
+ .name = "local-repository",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, local_repository),
+ .doc = "Directory where the repository local cache will be stored/read",
+ .arg_doc = "<directory>",
+ }, {
+ .id = 1001,
+ .name = "sync-strategy",
+ .type = >_sync_strategy,
+ .offset = offsetof(struct rpki_config, sync_strategy),
+ .doc = "RSYNC download strategy",
+ }, {
+ .id = 2000,
+ .name = "shuffle-uris",
+ .type = >_bool,
+ .offset = offsetof(struct rpki_config, shuffle_tal_uris),
+ .doc = "Shuffle URIs in the TAL before accessing them",
+ }, {
+ .id = 1002,
+ .name = "maximum-certificate-depth",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config,
+ maximum_certificate_depth),
+ .doc = "Maximum allowable certificate chain length",
+ .min = 1,
+ /**
+ * It cannot be UINT_MAX, because then the actual number will
+ * overflow and will never be bigger than this.
+ */
+ .max = UINT_MAX - 1,
+ },
+
+ /* Server fields */
+ {
+ .id = 5000,
+ .name = "server.address",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, server.address),
+ .doc = "The listener address of the RTR server.",
+ }, {
+ .id = 5001,
+ .name = "server.port",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, server.port),
+ .doc = "", /* TODO */
+ }, {
+ .id = 5003,
+ .name = "server.queue",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config, server.queue),
+ .doc = "Maximum accepted client connections",
+ .min = 1,
+ .max = SOMAXCONN,
+ }, {
+ .id = 5004,
+ .name = "server.vrps-check-interval",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config, server.vrps_check_interval),
+ .doc = "Interval used to look for updates at VRPs location",
+ /*
+ * RFC 6810 and 8210:
+ * The cache MUST rate-limit Serial Notifies to no more frequently than
+ * one per minute.
+ */
+ .min = 60,
+ .max = 7200,
+ }, {
+ .id = 5005,
+ .name = "server.rtr-interval.refresh",
+ .type = >_uint32,
+ .offset = offsetof(struct rpki_config, server.refresh_interval),
+ .doc = "Intervals use at RTR v1 End of data PDU",
+ .min = 1,
+ .max = 86400,
+ }, {
+ .id = 5006,
+ .name = "server.rtr-interval.retry",
+ .type = >_uint32,
+ .offset = offsetof(struct rpki_config, server.retry_interval),
+ .doc = "",
+ .min = 1,
+ .max = 7200,
+ }, {
+ .id = 5007,
+ .name = "server.rtr-interval.expire",
+ .type = >_uint32,
+ .offset = offsetof(struct rpki_config, server.expire_interval),
+ .doc = "",
+ .min = 600,
+ .max = 172800,
++ }, {
++ .id = 5008,
++ .name = "server.slurm.location",
++ .type = >_string,
++ .offset = offsetof(struct rpki_config, server.slurm_location),
++ .doc = "Directory where the .slurm files are located",
++ },
+
+ /* RSYNC fields */
+ {
+ .id = 3000,
+ .name = "rsync.program",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, rsync.program),
+ .doc = "Name of the program needed to execute an RSYNC",
+ .arg_doc = "<path to program>",
+ .availability = AVAILABILITY_JSON,
+ }, {
+ .id = 3001,
+ .name = "rsync.arguments-recursive",
+ .type = >_string_array,
+ .offset = offsetof(struct rpki_config, rsync.args.recursive),
+ .doc = "RSYNC program arguments that will trigger a recursive RSYNC",
+ .availability = AVAILABILITY_JSON,
+ }, {
+ .id = 3002,
+ .name = "rsync.arguments-flat",
+ .type = >_string_array,
+ .offset = offsetof(struct rpki_config, rsync.args.flat),
+ .doc = "RSYNC program arguments that will trigger a non-recursive RSYNC",
+ .availability = AVAILABILITY_JSON,
+ },
+
+ /* Logging fields */
+ {
+ .id = 'c',
+ .name = "log.color-output",
+ .type = >_bool,
+ .offset = offsetof(struct rpki_config, log.color),
+ .doc = "Print ANSI color codes.",
+ }, {
+ .id = 4000,
+ .name = "log.file-name-format",
+ .type = >_filename_format,
+ .offset = offsetof(struct rpki_config, log.filename_format),
+ .doc = "File name variant to print during debug/error messages",
+ },
+
+ { 0 },
+};
+
+/**
+ * Returns true if @field is the descriptor of one of the members of the
+ * struct rpki_config structure, false otherwise.
+ * (Alternatively: Returns true if @field->offset is valid, false otherwise.)
+ */
+static bool
+is_rpki_config_field(struct option_field const *field)
+{
+ return field->handler == NULL;
+}
+
+void *
+get_rpki_config_field(struct option_field const *field)
+{
+ return ((unsigned char *) &rpki_config) + field->offset;
+}
+
+static int
+handle_help(struct option_field const *field, char *arg)
+{
+ print_usage(stdout, true);
+ exit(0);
+}
+
+static int
+handle_usage(struct option_field const *field, char *arg)
+{
+ print_usage(stdout, false);
+ exit(0);
+}
+
+static int
+handle_version(struct option_field const *field, char *arg)
+{
+ printf("0.0.1\n");
+ exit(0);
+}
+
+static int
+handle_json(struct option_field const *field, char *file_name)
+{
+ return set_config_from_file(file_name);
+}
+
+static bool
+is_alphanumeric(int chara)
+{
+ return ('a' <= chara && chara <= 'z')
+ || ('A' <= chara && chara <= 'Z')
+ || ('0' <= chara && chara <= '9');
+}
+
+/**
+ * "struct option" is the array that getopt expects.
+ * "struct args_flag" is our option metadata.
+ */
+static int
+construct_getopt_options(struct option **_long_opts, char **_short_opts)
+{
+ struct option_field const *opt;
+ struct option *long_opts;
+ char *short_opts;
+ unsigned int total_long_options;
+ unsigned int total_short_options;
+
+ total_long_options = 0;
+ total_short_options = 0;
+ FOREACH_OPTION(options, opt, AVAILABILITY_GETOPT) {
+ total_long_options++;
+ if (is_alphanumeric(opt->id)) {
+ total_short_options++;
+ if (opt->type->has_arg != no_argument)
+ total_short_options++; /* ":" */
+ }
+ }
+
+ /* +1 NULL end, means end of array. */
+ long_opts = calloc(total_long_options + 1, sizeof(struct option));
+ if (long_opts == NULL)
+ return pr_enomem();
+ short_opts = malloc(total_short_options + 1);
+ if (short_opts == NULL) {
+ free(long_opts);
+ return pr_enomem();
+ }
+
+ *_long_opts = long_opts;
+ *_short_opts = short_opts;
+
+ FOREACH_OPTION(options, opt, AVAILABILITY_GETOPT) {
+ long_opts->name = opt->name;
+ long_opts->has_arg = opt->type->has_arg;
+ long_opts->flag = NULL;
+ long_opts->val = opt->id;
+ long_opts++;
+
+ if (is_alphanumeric(opt->id)) {
+ *short_opts = opt->id;
+ short_opts++;
+ if (opt->type->has_arg != no_argument) {
+ *short_opts = ':';
+ short_opts++;
+ }
+ }
+ }
+
+ *short_opts = '\0';
+ return 0;
+}
+
+static void
+print_config(void)
+{
+ struct option_field const *opt;
+
+ pr_info("Configuration {");
+ pr_indent_add();
+
+ FOREACH_OPTION(options, opt, 0xFFFF)
+ if (is_rpki_config_field(opt) && opt->type->print != NULL)
+ opt->type->print(opt, get_rpki_config_field(opt));
+
+ pr_indent_rm();
+ pr_info("}");
+}
+
+static int
+set_default_values(void)
+{
+ static char const *default_rsync_args[] = {
+ "--recursive",
+ "--delete",
+ "--times",
+ "--contimeout=20",
+ "$REMOTE",
+ "$LOCAL",
+ };
+
+ int error;
+
+ /*
+ * Values that might need to be freed WILL be freed, so use heap
+ * duplicates.
+ */
+
+ rpki_config.server.address = NULL;
+ rpki_config.server.port = strdup("323");
+ if (rpki_config.server.port == NULL)
+ return pr_enomem();
+
+ rpki_config.server.queue = 10;
+ rpki_config.server.vrps_check_interval = 60;
+ rpki_config.server.refresh_interval = 3600;
+ rpki_config.server.retry_interval = 600;
+ rpki_config.server.expire_interval = 7200;
++ rpki_config.server.slurm_location = NULL;
+
+ rpki_config.tal = NULL;
+
+ rpki_config.local_repository = strdup("repository/");
+ if (rpki_config.local_repository == NULL) {
+ error = pr_enomem();
+ goto revert_port;
+ }
+
+ rpki_config.sync_strategy = SYNC_ROOT;
+ rpki_config.shuffle_tal_uris = false;
+ rpki_config.maximum_certificate_depth = 32;
+
+ rpki_config.rsync.program = strdup("rsync");
+ if (rpki_config.rsync.program == NULL) {
+ error = pr_enomem();
+ goto revert_repository;
+ }
+
+ error = string_array_init(&rpki_config.rsync.args.recursive,
+ default_rsync_args, ARRAY_LEN(default_rsync_args));
+ if (error)
+ goto revert_rsync_program;
+ /* Simply remove --recursive and --delete. */
+ error = string_array_init(&rpki_config.rsync.args.flat,
+ default_rsync_args + 2, ARRAY_LEN(default_rsync_args) - 2);
+ if (error)
+ goto revert_recursive_array;
+
+ rpki_config.log.color = false;
+ rpki_config.log.filename_format = FNF_GLOBAL;
+
+ return 0;
+
+revert_recursive_array:
+ string_array_cleanup(&rpki_config.rsync.args.recursive);
+revert_rsync_program:
+ free(rpki_config.rsync.program);
+revert_repository:
+ free(rpki_config.local_repository);
+revert_port:
+ free(rpki_config.server.port);
+ return error;
+}
+
+static int
+validate_config(void)
+{
+ return (rpki_config.tal != NULL)
+ ? 0
+ : pr_err("The TAL file (--tal) is mandatory.");
+}
+
+static void
+print_usage(FILE *stream, bool print_doc)
+{
+ struct option_field const *option;
+ char const *arg_doc;
+
+ fprintf(stream, "Usage: %s\n", program_name);
+ FOREACH_OPTION(options, option, AVAILABILITY_GETOPT) {
+ fprintf(stream, "\t[");
+ fprintf(stream, "--%s", option->name);
+
+ if (option->arg_doc != NULL)
+ arg_doc = option->arg_doc;
+ else if (option->type->arg_doc != NULL)
+ arg_doc = option->type->arg_doc;
+ else
+ arg_doc = NULL;
+
+ switch (option->type->has_arg) {
+ case no_argument:
+ break;
+ case optional_argument:
+ case required_argument:
+ if (arg_doc != NULL)
+ fprintf(stream, "=%s", arg_doc);
+ break;
+ }
+
+ fprintf(stream, "]\n");
+
+ if (print_doc)
+ fprintf(stream, "\t (%s)\n", option->doc);
+ }
+}
+
+
+static int
+handle_opt(int opt)
+{
+ struct option_field const *option;
+
+ FOREACH_OPTION(options, option, AVAILABILITY_GETOPT) {
+ if (option->id == opt) {
+ return is_rpki_config_field(option)
+ ? option->type->parse.argv(option, optarg,
+ get_rpki_config_field(option))
+ : option->handler(option, optarg);
+ }
+ }
+
+ pr_err("Unrecognized option: %d", opt);
+ return -ESRCH;
+}
+
+int
+handle_flags_config(int argc, char **argv)
+{
+ struct option *long_opts;
+ char *short_opts;
+ int opt;
+ int error;
+
+ program_name = argv[0];
+ error = set_default_values();
+ if (error)
+ return error;
+
+ long_opts = NULL;
+ short_opts = NULL;
+ error = construct_getopt_options(&long_opts, &short_opts);
+ if (error)
+ goto end; /* Error msg already printed. */
+
+ while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL))
+ != -1) {
+ error = handle_opt(opt);
+ if (error)
+ goto end;
+ }
+
+ /*
+ * This triggers when the user runs something like
+ * `rpki-validator disable-rsync` instead of
+ * `rpki-validator --disable-rsync`.
+ * This program does not have unflagged payload.
+ */
+ if (optind < argc) {
+ error = pr_err("I don't know what '%s' is.", argv[optind]);
+ goto end;
+ }
+
+ error = validate_config();
+
+end:
+ if (error)
+ free_rpki_config();
+ else
+ print_config();
+
+ free(long_opts);
+ free(short_opts);
+ return error;
+
+}
+
+struct option_field const *
+get_option_metadatas(void)
+{
+ return options;
+}
+
+char const *
+config_get_server_address(void)
+{
+ return rpki_config.server.address;
+}
+
+char const *
+config_get_server_port(void)
+{
+ return rpki_config.server.port;
+}
+
+int
+config_get_server_queue(void)
+{
+ /*
+ * The range of this is 1-<small number>, so adding signedness is safe.
+ */
+ return rpki_config.server.queue;
+}
+
+unsigned int
+config_get_vrps_check_interval(void)
+{
+ return rpki_config.server.vrps_check_interval;
+}
+
+uint32_t
+config_get_refresh_interval(void)
+{
+ return rpki_config.server.refresh_interval;
+}
+
+uint32_t
+config_get_retry_interval(void)
+{
+ return rpki_config.server.retry_interval;
+}
+
+uint32_t
+config_get_expire_interval(void)
+{
+ return rpki_config.server.expire_interval;
+}
+
++char const *
++config_get_slurm_location(void)
++{
++ return rpki_config.server.slurm_location;
++}
+char const *
+config_get_tal(void)
+{
+ return rpki_config.tal;
+}
+
+char const *
+config_get_local_repository(void)
+{
+ return rpki_config.local_repository;
+}
+
+enum sync_strategy
+config_get_sync_strategy(void)
+{
+ return rpki_config.sync_strategy;
+}
+
+bool
+config_get_shuffle_tal_uris(void)
+{
+ return rpki_config.shuffle_tal_uris;
+}
+
+unsigned int
+config_get_max_cert_depth(void)
+{
+ return rpki_config.maximum_certificate_depth;
+}
+
+bool
+config_get_color_output(void)
+{
+ return rpki_config.log.color;
+}
+
+enum filename_format
+config_get_filename_format(void)
+{
+ return rpki_config.log.filename_format;
+}
+
+char *
+config_get_rsync_program(void)
+{
+ return rpki_config.rsync.program;
+}
+
+struct string_array const *
+config_get_rsync_args(bool is_ta)
+{
+ switch (rpki_config.sync_strategy) {
+ case SYNC_ROOT:
+ return &rpki_config.rsync.args.recursive;
+ case SYNC_ROOT_EXCEPT_TA:
+ return is_ta
+ ? &rpki_config.rsync.args.flat
+ : &rpki_config.rsync.args.recursive;
+ case SYNC_STRICT:
+ return &rpki_config.rsync.args.flat;
+ case SYNC_OFF:
+ break;
+ }
+
+ pr_crit("Invalid sync strategy: '%u'",
+ rpki_config.sync_strategy);
+ /*
+ * Return something usable anyway; don't want to check NULL.
+ * This is supposed to be unreachable code anyway.
+ */
+ return &rpki_config.rsync.args.recursive;
+}
+
+void
+free_rpki_config(void)
+{
+ struct option_field const *option;
+
+ FOREACH_OPTION(options, option, 0xFFFF)
+ if (is_rpki_config_field(option) && option->type->free != NULL)
+ option->type->free(get_rpki_config_field(option));
+}
--- /dev/null
+#ifndef SRC_CONFIG_H_
+#define SRC_CONFIG_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "config/filename_format.h"
+#include "config/sync_strategy.h"
+#include "config/string_array.h"
+#include "config/types.h"
+
+/* Init/destroy */
+int handle_flags_config(int , char **);
+void free_rpki_config(void);
+
+/* Getters */
+char const *config_get_server_address(void);
+char const *config_get_server_port(void);
+int config_get_server_queue(void);
+unsigned int config_get_vrps_check_interval(void);
+uint32_t config_get_refresh_interval(void);
+uint32_t config_get_retry_interval(void);
+uint32_t config_get_expire_interval(void);
++char const *config_get_slurm_location(void);
+
+char const *config_get_tal(void);
+char const *config_get_local_repository(void);
+enum sync_strategy config_get_sync_strategy(void);
+bool config_get_shuffle_tal_uris(void);
+unsigned int config_get_max_cert_depth(void);
+bool config_get_color_output(void);
+enum filename_format config_get_filename_format(void);
+char *config_get_rsync_program(void);
+struct string_array const *config_get_rsync_args(bool);
+
+/* Needed public by the JSON module */
+void *get_rpki_config_field(struct option_field const *);
+struct option_field const *get_option_metadatas(void);
+
+#endif /* SRC_CONFIG_H_ */
#include <openssl/err.h>
#include <openssl/evp.h>
+ #include <err.h>
+ #include <errno.h>
+ #include <string.h>
-/*
- * TODO This is a copy of fort-validator/src/crypto/base64.c with a few
- * modifications:
- * - At 'base64_decode': new parameter 'has_nl' to indicate if the encoded
- * string has newlines in it.
- * - New function 'base64url_decode'.
- */
-
/**
* Converts error from libcrypto representation to this project's
* representation.
* register a libcrypto stack error.
*/
int
- base64_decode(BIO *in, unsigned char *out, size_t out_len, size_t *out_written)
-base64_decode(BIO *in, unsigned char **out, bool has_nl, size_t out_len,
++base64_decode(BIO *in, unsigned char *out, bool has_nl, size_t out_len,
+ size_t *out_written)
{
BIO *b64;
size_t offset = 0;
return error ? error_ul2i(error) : 0;
}
- error = base64_decode(encoded, result, false, alloc_size, &dec_len);
+
+ /*
+ * Decode a base64 encoded string (@str_encoded), the decoded value is
+ * allocated at @result with a length of @result_len.
+ *
+ * Return 0 on success, or the error code if something went wrong. Don't forget
+ * to free @result after a successful decoding.
+ */
+ int
+ base64url_decode(char const *str_encoded, unsigned char **result,
+ size_t *result_len)
+ {
+ BIO *encoded; /* base64 encoded. */
+ char *str_copy;
+ size_t encoded_len, alloc_size, dec_len;
+ int error, pad, i;
+
+ /*
+ * Apparently there isn't a base64url decoder, and there isn't
+ * much difference between base64 codification and base64url, just as
+ * stated in RFC 4648 section 5: "This encoding is technically
+ * identical to the previous one, except for the 62:nd and 63:rd
+ * alphabet character, as indicated in Table 2".
+ *
+ * The existing base64 can be used if the 62:nd and 63:rd base64url
+ * alphabet chars are replaced with the corresponding base64 chars, and
+ * also if we add the optional padding that the member should have.
+ */
+ encoded_len = strlen(str_encoded);
+ pad = (encoded_len % 4) > 0 ? 4 - (encoded_len % 4) : 0;
+
+ str_copy = malloc(encoded_len + pad + 1);
+ if (str_copy == NULL)
+ return -ENOMEM;
+ /* Set all with pad char, then replace with the original string */
+ memset(str_copy, '=', encoded_len + pad);
+ memcpy(str_copy, str_encoded, encoded_len);
+ str_copy[encoded_len + pad] = '\0';
+
+ for (i = 0; i < encoded_len; i++) {
+ if (str_copy[i] == '-')
+ str_copy[i] = '+';
+ else if (str_copy[i] == '_')
+ str_copy[i] = '/';
+ }
+
+ /* Now decode as regular base64 */
+ encoded = BIO_new_mem_buf(str_copy, -1);
+ if (encoded == NULL) {
+ warnx("BIO_new() returned NULL");
+ error = -EINVAL;
+ goto free_copy;
+ }
+
+ alloc_size = EVP_DECODE_LENGTH(strlen(str_copy));
+ *result = malloc(alloc_size + 1);
+ if (*result == NULL) {
+ error = -ENOMEM;
+ goto free_enc;
+ }
+ memset(*result, 0, alloc_size);
+ (*result)[alloc_size] = '\0';
+
++ error = base64_decode(encoded, *result, false, alloc_size, &dec_len);
+ if (error)
+ goto free_all;
+
+ if (dec_len == 0) {
+ warnx("'%s' couldn't be decoded", str_encoded);
+ error = -EINVAL;
+ goto free_all;
+ }
+ *result_len = dec_len;
+
+ free(str_copy);
+ BIO_free(encoded);
+ return 0;
+ free_all:
+ free(*result);
+ free_enc:
+ BIO_free(encoded);
+ free_copy:
+ free(str_copy);
+ return error;
+ }
#include <stddef.h>
#include <openssl/bio.h>
- int base64_decode(BIO *, unsigned char *, size_t, size_t *);
-int base64_decode(BIO *, unsigned char **, bool, size_t, size_t *);
++int base64_decode(BIO *, unsigned char *, bool, size_t, size_t *);
+ int base64url_decode(char const *, unsigned char **, size_t *);
#endif /* SRC_BASE64_H_ */
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "rtr/rtr.h"
-#include "slurm_loader.h"
#include "clients.h"
-#include "configuration.h"
-#include "csv.h"
-#include "vrps.h"
+#include "config.h"
+#include "console_handler.h"
+#include "debug.h"
+#include "extension.h"
+#include "nid.h"
++#include "slurm_loader.h"
+#include "thread_var.h"
+#include "rsync/rsync.h"
+#include "rtr/rtr.h"
+#include "rtr/db/vrps.h"
-/*
- * This program is an RTR server.
- *
- * RTR ("RPKI-to-Router") is a protocol (defined in RFCs 6810 and 8210) that
- * reports the work of an RPKI validator (cryptographcally-verified
- * attestations that define the ASN that owns a given routing prefix). It is
- * normally served to routers who wish to verify BGP claims.
- */
-int
-main(int argc, char *argv[])
+static int
+start_rtr_server(void)
{
- int err;
- char *json_file = NULL;
- int c;
+ int error;
- while ((c = getopt(argc, argv, "f:")) != -1) {
- switch (c) {
- case 'f':
- json_file = optarg;
- break;
- case '?':
- fprintf(stdout, "usage: %s -f <file name>\n", argv[0]);
- return 0;
- }
- }
-
- if (json_file == NULL) {
- fprintf(stderr, "Missing flag '-f <file name>'\n");
- return -EINVAL;
- }
-
- err = config_init(json_file);
- if (err) {
- /*
- * TODO Special scenario, if the VRPs location doesn't exists
- * just send a warning (logged by the config_init function).
- *
- * This should be fixed later.
- */
- err = (err == -ENOENT ? 0 : err);
- goto end1;
- }
-
- err = deltas_db_init();
- if (err)
+ error = vrps_init();
+ if (error)
goto end1;
- err = clients_db_init();
- if (err)
+ error = clients_db_init();
+ if (error)
goto end2;
- err = csv_parse_vrps_file();
- if (err)
++ error = slurm_load();
++ if (error)
+ goto end3;
+
- err = slurm_load();
- if (err)
- goto end4;
-
- err = rtr_listen();
- if (err)
- goto end4;
+ error = rtr_listen();
+ rtr_cleanup(); /* TODO shouldn't this only happen on !error? */
- rtr_cleanup();
-end4:
- slurm_cleanup();
+ end3:
clients_db_destroy();
-end2:
- deltas_db_destroy();
-end1:
- config_cleanup();
- return err;
++ slurm_cleanup();
+end2: vrps_destroy();
+end1: return error;
+}
+
+int
+main(int argc, char **argv)
+{
+ int error;
+
+ print_stack_trace_on_segfault();
+
+ error = thvar_init();
+ if (error)
+ return error;
+
+ error = handle_flags_config(argc, argv);
+ if (error)
+ return error;
+
+ error = rsync_init();
+ if (error)
+ goto revert_config;
+ error = nid_init();
+ if (error)
+ goto revert_rsync;
+ error = extension_init();
+ if (error)
+ goto revert_rsync;
+
+ error = (config_get_server_address() != NULL)
+ ? start_rtr_server()
+ : validate_into_console();
+
+revert_rsync:
+ rsync_destroy();
+revert_config:
+ free_rpki_config();
+ return error;
}
--- /dev/null
- error = base64_decode(encoded, tal->spki, alloc_size, &tal->spki_len);
+#define _GNU_SOURCE
+
+#include "tal.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <openssl/evp.h>
+
+#include "common.h"
+#include "config.h"
+#include "line_file.h"
+#include "log.h"
+#include "random.h"
+#include "state.h"
+#include "thread_var.h"
+#include "crypto/base64.h"
+#include "object/certificate.h"
+#include "rsync/rsync.h"
+
+struct uris {
+ char **array; /* This is an array of string pointers. */
+ unsigned int count;
+ unsigned int size;
+};
+
+struct tal {
+ char const *file_name;
+ struct uris uris;
+ unsigned char *spki; /* Decoded; not base64. */
+ size_t spki_len;
+};
+
+static int
+uris_init(struct uris *uris)
+{
+ uris->count = 0;
+ uris->size = 4; /* Most TALs only define one. */
+ uris->array = malloc(uris->size * sizeof(char *));
+ return (uris->array != NULL) ? 0 : -ENOMEM;
+}
+
+static void
+uris_destroy(struct uris *uris)
+{
+ unsigned int i;
+ for (i = 0; i < uris->count; i++)
+ free(uris->array[i]);
+ free(uris->array);
+}
+
+static int
+uris_add(struct uris *uris, char *uri)
+{
+ char **tmp;
+
+ if (uris->count + 1 >= uris->size) {
+ uris->size *= 2;
+ tmp = realloc(uris->array, uris->size * sizeof(char *));
+ if (tmp == NULL)
+ return pr_enomem();
+ uris->array = tmp;
+ }
+
+ uris->array[uris->count++] = uri;
+ return 0;
+}
+
+static int
+read_uris(struct line_file *lfile, struct uris *uris)
+{
+ char *uri;
+ int error;
+
+ error = lfile_read(lfile, &uri);
+ if (error)
+ return error;
+
+ if (uri == NULL)
+ return pr_err("TAL file is empty.");
+ if (strcmp(uri, "") == 0) {
+ free(uri);
+ return pr_err("There's no URI in the first line of the TAL.");
+ }
+
+ error = uris_add(uris, uri);
+ if (error)
+ return error;
+
+ do {
+ error = lfile_read(lfile, &uri);
+ if (error)
+ return error;
+
+ if (uri == NULL)
+ return pr_err("TAL file ended prematurely. (Expected URI list, blank line and public key.)");
+ if (strcmp(uri, "") == 0) {
+ free(uri);
+ return 0; /* Happy path */
+ }
+
+ error = uris_add(uris, uri);
+ if (error)
+ return error;
+ } while (true);
+}
+
+/*
+ * Will usually allocate slightly more because of the newlines, but I'm fine
+ * with it.
+ */
+static size_t
+get_spki_alloc_size(struct line_file *lfile)
+{
+ struct stat st;
+ size_t result;
+
+ stat(lfile_name(lfile), &st);
+ result = st.st_size - lfile_offset(lfile);
+
+ return EVP_DECODE_LENGTH(result);
+}
+
+static int
+read_spki(struct line_file *lfile, struct tal *tal)
+{
+ BIO *encoded; /* base64 encoded. */
+ size_t alloc_size;
+ int error;
+
+ alloc_size = get_spki_alloc_size(lfile);
+ tal->spki = malloc(alloc_size);
+ if (tal->spki == NULL)
+ return -ENOMEM;
+
+ encoded = BIO_new_fp(lfile_fd(lfile), BIO_NOCLOSE);
+ if (encoded == NULL) {
+ free(tal->spki);
+ return crypto_err("BIO_new_fp() returned NULL");
+ }
+
++ error = base64_decode(encoded, tal->spki, true, alloc_size,
++ &tal->spki_len);
+ if (error)
+ free(tal->spki);
+
+ BIO_free(encoded);
+ return error;
+}
+
+/**
+ * @file_name is expected to outlive @result.
+ */
+int
+tal_load(char const *file_name, struct tal **result)
+{
+ struct line_file *lfile;
+ struct tal *tal;
+ int error;
+
+ error = lfile_open(file_name, &lfile);
+ if (error) {
+ pr_errno(error, "Error opening file '%s'", file_name);
+ goto fail4;
+ }
+
+ tal = malloc(sizeof(struct tal));
+ if (tal == NULL) {
+ error = -ENOMEM;
+ goto fail3;
+ }
+
+ tal->file_name = file_name;
+
+ error = uris_init(&tal->uris);
+ if (error)
+ goto fail2;
+
+ error = read_uris(lfile, &tal->uris);
+ if (error)
+ goto fail1;
+
+ error = read_spki(lfile, tal);
+ if (error)
+ goto fail1;
+
+ lfile_close(lfile);
+ *result = tal;
+ return 0;
+
+fail1:
+ uris_destroy(&tal->uris);
+fail2:
+ free(tal);
+fail3:
+ lfile_close(lfile);
+fail4:
+ return error;
+}
+
+void tal_destroy(struct tal *tal)
+{
+ if (tal == NULL)
+ return;
+
+ uris_destroy(&tal->uris);
+ free(tal->spki);
+ free(tal);
+}
+
+int
+foreach_uri(struct tal *tal, foreach_uri_cb cb, void *arg)
+{
+ struct rpki_uri uri;
+ unsigned int i;
+ int error;
+
+ for (i = 0; i < tal->uris.count; i++) {
+ error = uri_init_str(&uri, tal->uris.array[i],
+ strlen(tal->uris.array[i]));
+ if (error == ENOTRSYNC) {
+ /* Log level should probably be INFO. */
+ pr_debug("TAL has non-RSYNC URI; ignoring.");
+ continue;
+ }
+ if (error)
+ return error;
+
+ error = cb(tal, &uri, arg);
+ uri_cleanup(&uri);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+void
+tal_shuffle_uris(struct tal *tal)
+{
+ char **array = tal->uris.array;
+ unsigned int count = tal->uris.count;
+ char *tmp;
+ long random_index;
+ unsigned int i;
+
+ random_init();
+
+ for (i = 0; i < count; i++) {
+ tmp = array[i];
+ random_index = random_at_most(count - 1 - i) + i;
+ array[i] = array[random_index];
+ array[random_index] = tmp;
+ }
+}
+
+char const *
+tal_get_file_name(struct tal *tal)
+{
+ return tal->file_name;
+}
+
+void
+tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len)
+{
+ *buffer = tal->spki;
+ *len = tal->spki_len;
+}
+
+/**
+ * Performs the whole validation walkthrough on uri @uri, which is assumed to
+ * have been extracted from a TAL.
+ */
+static int
+handle_tal_uri(struct tal *tal, struct rpki_uri const *uri, void *arg)
+{
+ /*
+ * Because of the way the foreach iterates, this function must return
+ *
+ * - 0 on soft errors.
+ * - `> 0` on URI handled successfully.
+ * - `< 0` on hard errors.
+ *
+ * A "soft error" is "the connection to the preferred URI fails, or the
+ * retrieved CA certificate public key does not match the TAL public
+ * key." (RFC 7730)
+ *
+ * A "hard error" is any other error.
+ */
+
+ struct validation *state;
+ int error;
+
+ error = download_files(uri, true);
+ if (error) {
+ return pr_warn("TAL URI '%s' could not be RSYNC'd.",
+ uri->global);
+ }
+
+ error = validation_prepare(&state, tal, arg);
+ if (error)
+ return ENSURE_NEGATIVE(error);
+
+ error = vhandler_reset(arg);
+ if (error)
+ return ENSURE_NEGATIVE(error);
+
+ pr_debug_add("TAL URI '%s' {", uri_get_printable(uri));
+
+ if (!uri_is_certificate(uri)) {
+ pr_err("TAL file does not point to a certificate. (Expected .cer, got '%s')",
+ uri_get_printable(uri));
+ error = -EINVAL;
+ goto end;
+ }
+
+ error = certificate_traverse(NULL, uri, NULL, true);
+ if (error) {
+ switch (validation_pubkey_state(state)) {
+ case PKS_INVALID:
+ error = 0;
+ break;
+ case PKS_VALID:
+ case PKS_UNTESTED:
+ error = ENSURE_NEGATIVE(error);
+ break;
+ }
+ } else {
+ error = 1;
+ }
+
+end: validation_destroy(state);
+ pr_debug_rm("}");
+ return error;
+}
+
+int
+perform_standalone_validation(struct validation_handler *handler)
+{
+ struct tal *tal;
+ int error;
+
+ fnstack_init();
+ fnstack_push(config_get_tal());
+
+ error = tal_load(config_get_tal(), &tal);
+ if (error)
+ goto end;
+
+ if (config_get_shuffle_tal_uris())
+ tal_shuffle_uris(tal);
+ error = foreach_uri(tal, handle_tal_uri, handler);
+ if (error > 0)
+ error = 0;
+ else if (error == 0)
+ error = pr_err("None of the URIs of the TAL yielded a successful traversal.");
+
+end: tal_destroy(tal);
+ fnstack_pop();
+ fnstack_cleanup();
+ return error;
+}
struct pdu_header header;
};
+ struct router_key_pdu {
+ struct pdu_header header;
+ unsigned char *ski;
+ size_t ski_len;
+ u_int32_t asn;
+ unsigned char *spki;
+ size_t spki_len;
+ };
+
struct error_report_pdu {
struct pdu_header header;
- u_int32_t error_pdu_length;
+ uint32_t error_pdu_length;
void *erroneous_pdu;
- u_int32_t error_message_length;
+ uint32_t error_message_length;
rtr_char *error_message;
};
return len;
}
-static u_int32_t
-length_router_key_pdu(struct router_key_pdu *pdu)
-{
- return HEADER_LENGTH +
- pdu->ski_len + sizeof(pdu->asn) + pdu->spki_len;
-}
++/* TODO Include Router Key PDU serials */
++/**
++ * static uint32_t
++ * length_router_key_pdu(struct router_key_pdu *pdu)
++ * {
++ * return HEADER_LENGTH +
++ * pdu->ski_len + sizeof(pdu->asn) + pdu->spki_len;
++ * }
++ */
+
-static u_int32_t
+static uint32_t
length_error_report_pdu(struct error_report_pdu *pdu)
{
return HEADER_LENGTH +
}
int
-send_error_report_pdu(int fd, u_int8_t version, u_int16_t code,
-struct pdu_header *err_pdu_header, char *message)
+send_error_report_pdu(int fd, uint8_t version, uint16_t code,
+struct pdu_header *err_pdu_header, char const *message)
{
struct error_report_pdu pdu;
- char data[BUFFER_SIZE];
+ unsigned char data[BUFFER_SIZE];
size_t len;
set_header_values(&pdu.header, version, PDU_TYPE_ERROR_REPORT,
}
static size_t
- serialize_pdu_header(struct pdu_header *header, uint16_t union_value, char *buf)
-serialize_pdu_header(struct pdu_header *header, u_int16_t union_value,
++serialize_pdu_header(struct pdu_header *header, uint16_t union_value,
+ unsigned char *buf)
{
- char *ptr;
+ unsigned char *ptr;
ptr = buf;
ptr = write_int8(ptr, header->protocol_version);
}
size_t
- serialize_serial_notify_pdu(struct serial_notify_pdu *pdu, char *buf)
+ serialize_serial_notify_pdu(struct serial_notify_pdu *pdu, unsigned char *buf)
{
size_t head_size;
- char *ptr;
+ unsigned char *ptr;
- head_size = serialize_pdu_header(&pdu->header, pdu->header.session_id,
+ head_size = serialize_pdu_header(&pdu->header, pdu->header.m.session_id,
buf);
ptr = buf + head_size;
}
size_t
- serialize_cache_response_pdu(struct cache_response_pdu *pdu, char *buf)
+ serialize_cache_response_pdu(struct cache_response_pdu *pdu,
+ unsigned char *buf)
{
/* No payload to serialize */
- return serialize_pdu_header(&pdu->header, pdu->header.session_id, buf);
+ return serialize_pdu_header(&pdu->header, pdu->header.m.session_id,
+ buf);
}
size_t
- serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *pdu, char *buf)
+ serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *pdu, unsigned char *buf)
{
size_t head_size;
- char *ptr;
+ unsigned char *ptr;
- head_size = serialize_pdu_header(&pdu->header, pdu->header.reserved,
+ head_size = serialize_pdu_header(&pdu->header, pdu->header.m.reserved,
buf);
ptr = buf + head_size;
}
size_t
- serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *pdu, char *buf)
+ serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *pdu, unsigned char *buf)
{
size_t head_size;
- char *ptr;
+ unsigned char *ptr;
- head_size = serialize_pdu_header(&pdu->header, pdu->header.reserved,
+ head_size = serialize_pdu_header(&pdu->header, pdu->header.m.reserved,
buf);
ptr = buf + head_size;
}
size_t
- serialize_end_of_data_pdu(struct end_of_data_pdu *pdu, char *buf)
+ serialize_end_of_data_pdu(struct end_of_data_pdu *pdu, unsigned char *buf)
{
size_t head_size;
- char *ptr;
+ unsigned char *ptr;
- head_size = serialize_pdu_header(&pdu->header, pdu->header.session_id,
+ head_size = serialize_pdu_header(&pdu->header, pdu->header.m.session_id,
buf);
ptr = buf + head_size;
}
size_t
- serialize_cache_reset_pdu(struct cache_reset_pdu *pdu, char *buf)
+ serialize_cache_reset_pdu(struct cache_reset_pdu *pdu, unsigned char *buf)
{
/* No payload to serialize */
- return serialize_pdu_header(&pdu->header, pdu->header.reserved, buf);
+ return serialize_pdu_header(&pdu->header, pdu->header.m.reserved, buf);
}
+ /*
+ * Don't forget to use 'header->reserved' to set flags
+ */
size_t
- serialize_error_report_pdu(struct error_report_pdu *pdu, char *buf)
+ serialize_router_key_pdu(struct router_key_pdu *pdu, unsigned char *buf)
+ {
+ size_t head_size;
+ unsigned char *ptr;
+ int i;
+
+ if (pdu->header.protocol_version == RTR_V0)
+ return 0;
+
- head_size = serialize_pdu_header(&pdu->header, pdu->header.reserved,
++ head_size = serialize_pdu_header(&pdu->header, pdu->header.m.reserved,
+ buf);
+
+ ptr = buf + head_size;
+
+ for (i = 0; i < pdu->ski_len; i++)
+ ptr = write_int8(ptr, pdu->ski[i]);
+
+ ptr = write_int32(ptr, pdu->asn);
+
+ for (i = 0; i < pdu->spki_len; i++)
+ ptr = write_int8(ptr, pdu->spki[i]);
+
+ return ptr - buf;
+ }
+
+ size_t
+ serialize_error_report_pdu(struct error_report_pdu *pdu, unsigned char *buf)
{
struct pdu_header *err_pdu_header;
size_t head_size;
- char *ptr, *tmp_ptr;
+ unsigned char *ptr;
+ char *tmp_ptr;
int i;
- head_size = serialize_pdu_header(&pdu->header, pdu->header.error_code,
+ head_size = serialize_pdu_header(&pdu->header, pdu->header.m.error_code,
buf);
ptr = buf + head_size;
struct data_buffer {
size_t len;
size_t capacity;
- char *data;
+ unsigned char *data;
};
-__BEGIN_DECLS
void init_buffer(struct data_buffer *);
void free_buffer(struct data_buffer *);
- size_t serialize_serial_notify_pdu(struct serial_notify_pdu *, char *);
- size_t serialize_cache_response_pdu(struct cache_response_pdu *, char *);
- size_t serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *, char *);
- size_t serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *, char *);
- size_t serialize_end_of_data_pdu(struct end_of_data_pdu *, char *);
- size_t serialize_cache_reset_pdu(struct cache_reset_pdu *, char *);
- size_t serialize_error_report_pdu(struct error_report_pdu *, char *);
+ size_t serialize_serial_notify_pdu(struct serial_notify_pdu *,
+ unsigned char *);
+ size_t serialize_cache_response_pdu(struct cache_response_pdu *,
+ unsigned char *);
+ size_t serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *, unsigned char *);
+ size_t serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *, unsigned char *);
+ size_t serialize_end_of_data_pdu(struct end_of_data_pdu *, unsigned char *);
+ size_t serialize_cache_reset_pdu(struct cache_reset_pdu *, unsigned char *);
+ size_t serialize_router_key_pdu(struct router_key_pdu *, unsigned char *);
+ size_t serialize_error_report_pdu(struct error_report_pdu *, unsigned char *);
-__END_DECLS
#endif /* SRC_RTR_PDU_SERIALIZER_H_ */
#include "primitive_writer.h"
- char *
- write_int8(char *buf, uint8_t value)
+ unsigned char *
-write_int8(unsigned char *buf, u_int8_t value)
++write_int8(unsigned char *buf, uint8_t value)
{
buf[0] = value;
return buf + 1;
}
/** Big Endian. */
- char *
- write_int16(char *buf, uint16_t value)
+ unsigned char *
-write_int16(unsigned char *buf, u_int16_t value)
++write_int16(unsigned char *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value;
}
/** Big Endian. */
- char *
- write_int32(char *buf, uint32_t value)
+ unsigned char *
-write_int32(unsigned char *buf, u_int32_t value)
++write_int32(unsigned char *buf, uint32_t value)
{
buf[0] = value >> 24;
buf[1] = value >> 16;
#include <netinet/in.h>
- char *write_int8(char *, uint8_t);
- char *write_int16(char *, uint16_t);
- char *write_int32(char *, uint32_t);
- char *write_in_addr(char *, struct in_addr);
- char *write_in6_addr(char *, struct in6_addr);
-__BEGIN_DECLS
-unsigned char *write_int8(unsigned char *, u_int8_t);
-unsigned char *write_int16(unsigned char *, u_int16_t);
-unsigned char *write_int32(unsigned char *, u_int32_t);
++unsigned char *write_int8(unsigned char *, uint8_t);
++unsigned char *write_int16(unsigned char *, uint16_t);
++unsigned char *write_int32(unsigned char *, uint32_t);
+ unsigned char *write_in_addr(unsigned char *, struct in_addr);
+ unsigned char *write_in6_addr(unsigned char *, struct in6_addr);
-__END_DECLS
#endif /* RTR_PRIMITIVE_WRITER_H_ */
--- /dev/null
-#include "array_list.h"
+ #include "slurm_db.h"
+
+ #include <stdbool.h>
+ #include <string.h>
++
++#include "data_structure/array_list.h"
+
+ ARRAY_LIST(al_filter_prefix, struct slurm_prefix)
+ ARRAY_LIST(al_assertion_prefix, struct slurm_prefix)
+ ARRAY_LIST(al_filter_bgpsec, struct slurm_bgpsec)
+ ARRAY_LIST(al_assertion_bgpsec, struct slurm_bgpsec)
+
+ struct arraylist_db {
+ struct al_filter_prefix filter_pfx_al;
+ struct al_assertion_prefix assertion_pfx_al;
+ struct al_filter_bgpsec filter_bgps_al;
+ struct al_assertion_bgpsec assertion_bgps_al;
+ } array_lists_db;
+
+ #define LOCATE_FUNCS(name, type, array_list, equal_cb, filter) \
+ static type * \
+ name##_locate(array_list *base, type *obj) \
+ { \
+ type *cursor; \
+ \
+ ARRAYLIST_FOREACH(base, cursor) \
+ if (equal_cb(cursor, obj, filter)) \
+ return cursor; \
+ \
+ return NULL; \
+ } \
+ \
+ static bool \
+ name##_exists(array_list *base, type *obj) \
+ { \
+ return name##_locate(base, obj) != NULL; \
+ }
+
+ int
+ slurm_db_init(void)
+ {
+ al_filter_prefix_init(&array_lists_db.filter_pfx_al);
+ al_assertion_prefix_init(&array_lists_db.assertion_pfx_al);
+ al_filter_bgpsec_init(&array_lists_db.filter_bgps_al);
+ al_assertion_bgpsec_init(&array_lists_db.assertion_bgps_al);
+
+ return 0;
+ }
+
+ static bool
+ prefix_filtered_by(struct slurm_prefix *prefix, struct slurm_prefix *filter)
+ {
+ /* Both have ASN */
+ if ((prefix->data_flag & SLURM_COM_FLAG_ASN) > 0 &&
+ (filter->data_flag & SLURM_COM_FLAG_ASN) > 0)
+ return prefix->asn == filter->asn;
+
+ /* Both have a prefix of the same type */
+ if ((prefix->data_flag & SLURM_PFX_FLAG_PREFIX) > 0 &&
+ (filter->data_flag & SLURM_PFX_FLAG_PREFIX) > 0 &&
+ prefix->addr_fam == filter->addr_fam &&
+ prefix->prefix_length == filter->prefix_length)
+ return ((prefix->addr_fam == AF_INET &&
+ prefix->ipv4_prefix.s_addr == filter->ipv4_prefix.s_addr) ||
+ (prefix->addr_fam == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(prefix->ipv6_prefix.s6_addr32,
+ filter->ipv6_prefix.s6_addr32)));
+
+ return false;
+ }
+
+ static bool
+ prefix_equal(struct slurm_prefix *left, struct slurm_prefix *right,
+ bool filter)
+ {
+ bool equal;
+
+ /* Ignore the comments */
+ if ((left->data_flag & ~SLURM_COM_FLAG_COMMENT) !=
+ (right->data_flag & ~SLURM_COM_FLAG_COMMENT))
+ return filter && prefix_filtered_by(left, right);
+
+ /* It has the same data, compare it */
+ equal = true;
+ if ((left->data_flag & SLURM_COM_FLAG_ASN) > 0)
+ equal = equal && left->asn == right->asn;
+
+ if ((left->data_flag & SLURM_PFX_FLAG_PREFIX) > 0)
+ equal = equal && left->prefix_length == right->prefix_length
+ && left->addr_fam == right->addr_fam
+ && ((left->addr_fam == AF_INET
+ && left->ipv4_prefix.s_addr == right->ipv4_prefix.s_addr)
+ || (left->addr_fam == AF_INET6
+ && IN6_ARE_ADDR_EQUAL(left->ipv6_prefix.s6_addr32,
+ right->ipv6_prefix.s6_addr32)));
+
+ if ((left->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0)
+ equal = equal &&
+ ((left->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0) &&
+ left->max_prefix_length == right->max_prefix_length;
+
+ return equal;
+ }
+
+ static bool
+ bgpsec_filtered_by(struct slurm_bgpsec *bgpsec, struct slurm_bgpsec *filter)
+ {
+ /* Both have ASN */
+ if ((bgpsec->data_flag & SLURM_COM_FLAG_ASN) > 0 &&
+ (filter->data_flag & SLURM_COM_FLAG_ASN) > 0)
+ return bgpsec->asn == filter->asn;
+
+ /* Both have a SKI */
+ if ((bgpsec->data_flag & SLURM_BGPS_FLAG_SKI) > 0 &&
+ (filter->data_flag & SLURM_BGPS_FLAG_SKI) > 0 &&
+ bgpsec->ski_len == filter->ski_len)
+ return memcmp(bgpsec->ski, filter->ski, bgpsec->ski_len) == 0;
+
+ return false;
+ }
+
+ static bool
+ bgpsec_equal(struct slurm_bgpsec *left, struct slurm_bgpsec *right,
+ bool filter)
+ {
+ bool equal;
+
+ /* Ignore the comments */
+ if ((left->data_flag & ~SLURM_COM_FLAG_COMMENT) !=
+ (right->data_flag & ~SLURM_COM_FLAG_COMMENT))
+ return filter && bgpsec_filtered_by(left, right);
+
+ /* It has the same data, compare it */
+ equal = true;
+ if ((left->data_flag & SLURM_COM_FLAG_ASN) > 0)
+ equal = equal && left->asn == right->asn;
+
+ if ((left->data_flag & SLURM_BGPS_FLAG_SKI) > 0)
+ equal = equal && left->ski_len == right->ski_len &&
+ memcmp(left->ski, right->ski, left->ski_len) == 0;
+
+ if ((left->data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) > 0)
+ equal = equal &&
+ left->router_public_key_len ==
+ right->router_public_key_len &&
+ memcmp(left->router_public_key, right->router_public_key,
+ left->router_public_key_len) == 0;
+
+ return equal;
+ }
+
+ LOCATE_FUNCS(prefix_filter, struct slurm_prefix, struct al_filter_prefix,
+ prefix_equal, true)
+ LOCATE_FUNCS(bgpsec_filter, struct slurm_bgpsec, struct al_filter_bgpsec,
+ bgpsec_equal, true)
+ LOCATE_FUNCS(prefix_assertion, struct slurm_prefix, struct al_assertion_prefix,
+ prefix_equal, false)
+ LOCATE_FUNCS(bgpsec_assertion, struct slurm_bgpsec, struct al_assertion_bgpsec,
+ bgpsec_equal, false)
+
+ /*
+ * Try to persist the @prefix filter, if it already exists or is covered
+ * by another filter, then the error -EEXIST is returned; otherwise, returns
+ * the result of persisting the @prefix.
+ */
+ int
+ slurm_db_add_prefix_filter(struct slurm_prefix *prefix)
+ {
+ if (prefix_filter_exists(&array_lists_db.filter_pfx_al, prefix))
+ return -EEXIST;
+
+ return al_filter_prefix_add(&array_lists_db.filter_pfx_al, prefix);
+ }
+
+ /*
+ * Try to persist the @prefix assertion, if it already exists, then the error
+ * -EEXIST is returned; otherwise, returns the result of persisting the
+ * @prefix.
+ */
+ int
+ slurm_db_add_prefix_assertion(struct slurm_prefix *prefix)
+ {
+ if (prefix_assertion_exists(&array_lists_db.assertion_pfx_al, prefix))
+ return -EEXIST;
+
+ return al_assertion_prefix_add(&array_lists_db.assertion_pfx_al,
+ prefix);
+ }
+
+ /*
+ * Try to persist the @bgpsec filter, if it already exists or is covered
+ * by another filter, then the error -EEXIST is returned; otherwise, returns
+ * the result of persisting the @bgpsec.
+ */
+ int
+ slurm_db_add_bgpsec_filter(struct slurm_bgpsec *bgpsec)
+ {
+ if (bgpsec_filter_exists(&array_lists_db.filter_bgps_al, bgpsec))
+ return -EEXIST;
+
+ return al_filter_bgpsec_add(&array_lists_db.filter_bgps_al, bgpsec);
+ }
+
+ /*
+ * Try to persist the @bgpsec assertion, if it already exists, then the error
+ * -EEXIST is returned; otherwise, returns the result of persisting the
+ * @bgpsec.
+ */
+ int
+ slurm_db_add_bgpsec_assertion(struct slurm_bgpsec *bgpsec)
+ {
+ if (bgpsec_assertion_exists(&array_lists_db.assertion_bgps_al, bgpsec))
+ return -EEXIST;
+
+ return al_assertion_bgpsec_add(&array_lists_db.assertion_bgps_al,
+ bgpsec);
+ }
+
+ static void
+ clean_slurm_prefix(struct slurm_prefix *prefix)
+ {
+ if ((prefix->data_flag & SLURM_COM_FLAG_COMMENT) > 0)
+ free((void *)prefix->comment);
+ }
+
+ static void
+ clean_slurm_bgpsec(struct slurm_bgpsec *bgpsec)
+ {
+ if ((bgpsec->data_flag & SLURM_BGPS_FLAG_SKI) > 0)
+ free(bgpsec->ski);
+ if ((bgpsec->data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) > 0)
+ free(bgpsec->router_public_key);
+ if ((bgpsec->data_flag & SLURM_COM_FLAG_COMMENT) > 0)
+ free((void *)bgpsec->comment);
+ }
+
+ void
+ slurm_db_cleanup(void)
+ {
+ al_filter_prefix_cleanup(&array_lists_db.filter_pfx_al,
+ clean_slurm_prefix);
+ al_filter_bgpsec_cleanup(&array_lists_db.filter_bgps_al,
+ clean_slurm_bgpsec);
+ al_assertion_prefix_cleanup(&array_lists_db.assertion_pfx_al,
+ clean_slurm_prefix);
+ al_assertion_bgpsec_cleanup(&array_lists_db.assertion_bgps_al,
+ clean_slurm_bgpsec);
+ }
--- /dev/null
-#include "configuration.h"
+ #include "slurm_loader.h"
+
+ #include <err.h>
+ #include <errno.h>
+ #include <dirent.h>
+ #include <stdlib.h>
+ #include <string.h>
+
++#include "config.h"
+ #include "slurm_db.h"
+ #include "slurm_parser.h"
+
+ #define SLURM_FILE_EXTENSION ".slurm"
+
+ static int
+ single_slurm_load(const char *dir_name, const char *file_name)
+ {
+ char *ext, *fullpath, *tmp;
+ int error;
+
+ ext = strrchr(file_name, '.');
+ /* Ignore file if extension isn't the expected */
+ if (ext == NULL || strcmp(ext, SLURM_FILE_EXTENSION) != 0)
+ return 0;
+
+ /* Get the full file path */
+ tmp = strdup(dir_name);
+ if (tmp == NULL) {
+ warn("Couldn't create temporal char for SLURM");
+ return -errno;
+ }
+ tmp = realloc(tmp, strlen(tmp) + 1 + strlen(file_name) + 1);
+ if (tmp == NULL) {
+ warn("Couldn't reallocate temporal char for SLURM");
+ return -errno;
+ }
+
+ strcat(tmp, "/");
+ strcat(tmp, file_name);
+ fullpath = realpath(tmp, NULL);
+ if (fullpath == NULL) {
+ warn("Error getting real path for file '%s' at dir '%s'",
+ dir_name, file_name);
+ free(tmp);
+ return -errno;
+ }
+
+ error = slurm_parse(fullpath);
+ free(tmp);
+ free(fullpath);
+ return error;
+ }
+
+ int
+ slurm_load(void)
+ {
+ DIR *dir_loc;
+ struct dirent *dir_ent;
+ char const *slurm_dir;
+ int error;
+
+ /* Optional configuration */
+ slurm_dir = config_get_slurm_location();
+ if (slurm_dir == NULL)
+ return 0;
+
+ error = slurm_db_init();
+ if (error)
+ return error;
+
+ dir_loc = opendir(slurm_dir);
+ if (dir_loc == NULL) {
+ warn("Couldn't open dir %s", slurm_dir);
+ return -errno;
+ }
+
+ errno = 0;
+ while ((dir_ent = readdir(dir_loc)) != NULL) {
+ error = single_slurm_load(slurm_dir, dir_ent->d_name);
+ if (error) {
+ warnx("The error was at SLURM file %s",
+ dir_ent->d_name);
+ goto end;
+ }
+ errno = 0;
+ }
+ if (errno) {
+ warn("Error reading dir %s", slurm_dir);
+ error = -errno;
+ }
+ end:
+ closedir(dir_loc);
+ return error;
+ }
+
+ void
+ slurm_cleanup(void)
+ {
+ /* Only if the SLURM was configured */
+ if (config_get_slurm_location() != NULL)
+ slurm_db_cleanup();
+ }
--- /dev/null
- return prefix4_decode(text, prefixv4);
+ #include "slurm_parser.h"
+
+ #include <err.h>
+ #include <errno.h>
+ #include <stdbool.h>
+ #include <stdint.h>
+ #include <string.h>
+ #include <openssl/evp.h>
+
+ #include "crypto/base64.h"
+ #include "address.h"
+ #include "json_parser.h"
+ #include "slurm_db.h"
+
+ /* JSON members */
+ #define SLURM_VERSION "slurmVersion"
+ #define VALIDATION_OUTPUT_FILTERS "validationOutputFilters"
+ #define PREFIX_FILTERS "prefixFilters"
+ #define BGPSEC_FILTERS "bgpsecFilters"
+ #define LOCALLY_ADDED_ASSERTIONS "locallyAddedAssertions"
+ #define PREFIX_ASSERTIONS "prefixAssertions"
+ #define BGPSEC_ASSERTIONS "bgpsecAssertions"
+
+ /* Prefix and BGPsec properties */
+ #define PREFIX "prefix"
+ #define ASN "asn"
+ #define MAX_PREFIX_LENGTH "maxPrefixLength"
+ #define SKI "SKI"
+ #define ROUTER_PUBLIC_KEY "routerPublicKey"
+ #define COMMENT "comment"
+
+ #define CHECK_REQUIRED(element, name) \
+ if (element == NULL) { \
+ warnx("SLURM member '%s' is required", name); \
+ return -EINVAL; \
+ }
+
+ static int handle_json(json_t *);
+
+ int
+ slurm_parse(char const *location)
+ {
+ json_t *json_root;
+ json_error_t json_error;
+ int error;
+
+ json_root = json_load_file(location, JSON_REJECT_DUPLICATES,
+ &json_error);
+ if (json_root == NULL) {
+ warnx("SLURM JSON error on line %d, column %d: %s",
+ json_error.line, json_error.column, json_error.text);
+ return -ENOENT;
+ }
+
+ error = handle_json(json_root);
+
+ json_decref(json_root);
+ return error;
+ }
+
+ /*
+ * TODO Maybe some of the parsing functions can be on a common place, since
+ * csv.c also does a similar parsing
+ */
+
+ static int
+ parse_prefix4(char *text, struct ipv4_prefix *prefixv4)
+ {
+ if (text == NULL)
+ return -EINVAL;
- return prefix6_decode(text, prefixv6);
++ return str_to_prefix4(text, prefixv4);
+ }
+
+ static int
+ parse_prefix6(char *text, struct ipv6_prefix *prefixv6)
+ {
+ if (text == NULL)
+ return -EINVAL;
-parse_prefix_length(char *text, unsigned int *value, int max_value)
++ return str_to_prefix6(text, prefixv6);
+ }
+
+ static int
- return prefix_length_decode(text, value, max_value);
++parse_prefix_length(char *text, uint8_t *value, uint8_t max_value)
+ {
+ if (text == NULL)
+ return -EINVAL;
- isv4 ? &prefixv4.len : &prefixv6.len,
- isv4 ? 32 : 128);
++ return str_to_prefix_length(text, value, max_value);
+ }
+ /*
+ * Any unknown members should be treated as errors, RFC8416 3.1:
+ * "JSON members that are not defined here MUST NOT be used in SLURM
+ * files. An RP MUST consider any deviations from the specifications to
+ * be errors."
+ */
+ static bool
+ valid_members_count(json_t *object, size_t expected_size)
+ {
+ return json_object_size(object) == expected_size;
+ }
+
+ static int
+ set_asn(json_t *object, bool is_assertion, u_int32_t *result,
+ u_int8_t *flag, size_t *members_loaded)
+ {
+ json_int_t int_tmp;
+ int error;
+
+ error = json_get_int(object, ASN, &int_tmp);
+ if (error == -ENOENT) {
+ if (is_assertion) {
+ warnx("ASN is required");
+ return -EINVAL;
+ } else
+ return 0; /* Optional for filters */
+ } else if (error)
+ return error;
+
+ /* An underflow or overflow will be considered here */
+ if (int_tmp < 0 || UINT32_MAX < int_tmp) {
+ warnx("ASN (%lld) is out of range [0 - %u].", int_tmp,
+ UINT32_MAX);
+ return -EINVAL;
+ }
+ *flag = *flag | SLURM_COM_FLAG_ASN;
+ *result = (u_int32_t) int_tmp;
+ (*members_loaded)++;
+ return 0;
+ }
+
+ static int
+ set_comment(json_t *object, char const **comment, u_int8_t *flag,
+ size_t *members_loaded)
+ {
+ char const *tmp;
+ int error;
+
+ error = json_get_string(object, COMMENT, &tmp);
+ if (error && error == -ENOENT)
+ return 0; /* Optional member */
+ else if (error)
+ return error;
+
+ *comment = strdup(tmp);
+ *flag = *flag | SLURM_COM_FLAG_COMMENT;
+ (*members_loaded)++;
+
+ return 0;
+ }
+
+ static int
+ set_prefix(json_t *object, bool is_assertion, struct slurm_prefix *result,
+ size_t *members_loaded)
+ {
+ struct ipv4_prefix prefixv4;
+ struct ipv6_prefix prefixv6;
+ char const *str_prefix;
+ char *clone, *token;
+ bool isv4;
+ int error;
+
+ /* First part: Prefix in string format */
+ error = json_get_string(object, PREFIX, &str_prefix);
+ if (error && error == -ENOENT) {
+ if (is_assertion) {
+ warnx("SLURM assertion prefix is required");
+ return -EINVAL;
+ } else
+ return 0; /* Optional for filters */
+ } else if (error)
+ return error;
+
+ clone = strdup(str_prefix);
+ if (clone == NULL) {
+ warn("Couldn't allocate string to parse prefix");
+ return -errno;
+ }
+
+ token = strtok(clone, "/");
+ isv4 = strchr(token, ':') == NULL;
+ if (isv4)
+ error = parse_prefix4(token, &prefixv4);
+ else
+ error = parse_prefix6(token, &prefixv6);
+
+ if (error) {
+ free(clone);
+ return error;
+ }
+
+ /* Second part: Prefix length in numeric format */
+ token = strtok(NULL, "/");
+ error = parse_prefix_length(token,
- error = prefix4_validate(&prefixv4);
++ (isv4 ? &prefixv4.len : &prefixv6.len),
++ (isv4 ? 32 : 128));
+ free(clone);
+ if (error)
+ return error;
+
+ if (isv4) {
- error = prefix6_validate(&prefixv6);
++ error = ipv4_prefix_validate(&prefixv4);
+ if (error)
+ return error;
+ result->addr_fam = AF_INET;
+ result->ipv4_prefix = prefixv4.addr;
+ result->prefix_length = prefixv4.len;
+ } else {
++ error = ipv6_prefix_validate(&prefixv6);
+ if (error)
+ return error;
+ result->addr_fam = AF_INET6;
+ result->ipv6_prefix = prefixv6.addr;
+ result->prefix_length = prefixv6.len;
+ }
+ result->data_flag |= SLURM_PFX_FLAG_PREFIX;
+ (*members_loaded)++;
+ return 0;
+ }
+
+ static int
+ set_max_prefix_length(json_t *object, bool is_assertion, u_int8_t addr_fam,
+ u_int8_t *result, u_int8_t *flag, size_t *members_loaded)
+ {
+ json_int_t int_tmp;
+ int error;
+
+ error = json_get_int(object, MAX_PREFIX_LENGTH, &int_tmp);
+ if (error == -ENOENT)
+ return 0; /* Optional for assertions, unsupported by filters */
+
+ if (error && is_assertion)
+ return error;
+
+ /* Unsupported by filters */
+ if (!is_assertion) {
+ warnx("Prefix filter can't have a max prefix length");
+ return -EINVAL;
+ }
+
+ /* An underflow or overflow will be considered here */
+ if (int_tmp <= 0 || (addr_fam == AF_INET ? 32 : 128) < int_tmp) {
+ warnx("Max prefix length (%lld) is out of range [1 - %d].",
+ int_tmp, (addr_fam == AF_INET ? 32 : 128));
+ return -EINVAL;
+ }
+ *flag = *flag | SLURM_PFX_FLAG_MAX_LENGTH;
+ *result = (u_int8_t) int_tmp;
+ (*members_loaded)++;
+ return 0;
+
+ }
+
+ int
+ validate_base64url_encoded(const char *encoded)
+ {
+ /*
+ * RFC 8416, sections 3.3.2 (SKI member), and 3.4.2 (SKI and
+ * routerPublicKey members): "{..} whose value is the Base64 encoding
+ * without trailing '=' (Section 5 of [RFC4648])"
+ */
+ if (strrchr(encoded, '=') != NULL) {
+ warnx("The base64 encoded value has trailing '='");
+ return -EINVAL;
+ }
+
+ /*
+ * IMHO there's an error at RFC 8416 regarding the use of base64
+ * encoding. The RFC cites "RFC 4648 section 5" to justify the
+ * removal of trailing pad char '=', a section that refers to base64url
+ * encoding. So, at the same RFC 4648 section, there's this paragraph:
+ * "This encoding may be referred to as "base64url". This encoding
+ * should not be regarded as the same as the "base64" encoding and
+ * should not be referred to as only "base64". Unless clarified
+ * otherwise, "base64" refers to the base 64 in the previous section."
+ *
+ * Well, I believe that the RFC 8416 must say something like:
+ * "{..} whose value is the Base64url encoding without trailing '='
+ * (Section 5 of [RFC4648])"
+ */
+ return 0;
+ }
+
+ static int
+ set_ski(json_t *object, bool is_assertion, struct slurm_bgpsec *result,
+ size_t *members_loaded)
+ {
+ char const *str_encoded;
+ int error;
+
+ error = json_get_string(object, SKI, &str_encoded);
+ if (error && error == -ENOENT) {
+ if (is_assertion) {
+ warnx("SLURM assertion %s is required", SKI);
+ return -EINVAL;
+ } else
+ return 0; /* Optional for filters */
+ } else if (error)
+ return error;
+
+ error = validate_base64url_encoded(str_encoded);
+ if (error)
+ return error;
+
+ error = base64url_decode(str_encoded, &result->ski, &result->ski_len);
+ if (error)
+ return error;
+
+ /* Validate that's at least 20 octects long */
+ if (result->ski_len != 20) {
+ warnx("The decoded SKI must be 20 octets long");
+ free(result->ski);
+ return -EINVAL;
+ }
+
+ result->data_flag = result->data_flag | SLURM_BGPS_FLAG_SKI;
+ (*members_loaded)++;
+ return 0;
+ }
+
+ static int
+ set_router_pub_key(json_t *object, bool is_assertion,
+ struct slurm_bgpsec *result, size_t *members_loaded)
+ {
+ char const *str_encoded;
+ int error;
+
+ error = json_get_string(object, ROUTER_PUBLIC_KEY, &str_encoded);
+ if (error == -ENOENT && !is_assertion)
+ return 0; /* OK for filters */
+
+ /* Required by assertions */
+ if (error && is_assertion) {
+ if (error == -ENOENT) {
+ warnx("SLURM assertion %s is required", ROUTER_PUBLIC_KEY);
+ return -EINVAL;
+ }
+ return error;
+ }
+
+ /* Unsupported by filters */
+ if (!is_assertion) {
+ warnx("BGPsec filter can't have a router public key");
+ return -EINVAL;
+ }
+
+ error = validate_base64url_encoded(str_encoded);
+ if (error)
+ return error;
+
+ error = base64url_decode(str_encoded, &result->router_public_key,
+ &result->router_public_key_len);
+ if (error)
+ return error;
+
+ /*
+ * TODO Validate that 'routerPublicKey' is: "the equivalent to the
+ * subjectPublicKeyInfo value of the router certificate's public key,
+ * as described in [RFC8208]. This is the full ASN.1 DER encoding of
+ * the subjectPublicKeyInfo, including the ASN.1 tag and length values
+ * of the subjectPublicKeyInfo SEQUENCE.
+ */
+ /*
+ * TODO When the merge is done, reuse the functions at fort-validator
+ *
+ * #include <libcmscodec/SubjectPublicKeyInfo.h>
+ * #include "asn1/decode.h"
+ * struct SubjectPublicKeyInfo *router_pki;
+ * error = asn1_decode(result->router_public_key,
+ * result->router_public_key_len, &asn_DEF_SubjectPublicKeyInfo,
+ * (void **) &router_pki);
+ */
+
+ result->data_flag = result->data_flag | SLURM_BGPS_FLAG_ROUTER_KEY;
+ (*members_loaded)++;
+ return 0;
+ }
+
+ static void
+ init_slurm_prefix(struct slurm_prefix *slurm_prefix)
+ {
+ slurm_prefix->data_flag = SLURM_COM_FLAG_NONE;
+ slurm_prefix->asn = 0;
+ slurm_prefix->ipv6_prefix = in6addr_any;
+ slurm_prefix->prefix_length = 0;
+ slurm_prefix->max_prefix_length = 0;
+ slurm_prefix->addr_fam = 0;
+ slurm_prefix->comment = NULL;
+ }
+
+ static int
+ load_single_prefix(json_t *object, bool is_assertion)
+ {
+ struct slurm_prefix result;
+ size_t member_count;
+ int error;
+
+ if (!json_is_object(object)) {
+ warnx("Not a valid JSON object");
+ return -EINVAL;
+ }
+
+ init_slurm_prefix(&result);
+ member_count = 0;
+
+ error = set_asn(object, is_assertion, &result.asn, &result.data_flag,
+ &member_count);
+ if (error)
+ return error;
+
+ error = set_prefix(object, is_assertion, &result, &member_count);
+ if (error)
+ return error;
+
+ error = set_max_prefix_length(object, is_assertion, result.addr_fam,
+ &result.max_prefix_length, &result.data_flag, &member_count);
+ if (error)
+ return error;
+
+ error = set_comment(object, &result.comment, &result.data_flag,
+ &member_count);
+ if (error)
+ return error;
+
+ /* A single comment isn't valid */
+ if (result.data_flag == SLURM_COM_FLAG_COMMENT) {
+ warnx("Single comments aren't valid");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ /* A filter must have ASN and/or prefix */
+ if (!is_assertion) {
+ if ((result.data_flag &
+ (SLURM_COM_FLAG_ASN | SLURM_PFX_FLAG_PREFIX)) == 0) {
+ warnx("Prefix filter must have an asn and/or prefix");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ /* Validate expected members */
+ if (!valid_members_count(object, member_count)) {
+ warnx("Prefix filter has unknown members (see RFC 8416 section 3.3.1)");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ error = slurm_db_add_prefix_filter(&result);
+ if (error)
+ goto release_comment;
+
+ return 0;
+ }
+
+ /*
+ * An assertion must have ASN and prefix, the validation is done at
+ * set_asn and set_prefix
+ */
+
+ if ((result.data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0)
+ if (result.prefix_length > result.max_prefix_length) {
+ warnx(
+ "Prefix length is greater than max prefix length");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ /* Validate expected members */
+ if (!valid_members_count(object, member_count)) {
+ warnx("Prefix assertion has unknown members (see RFC 8416 section 3.4.1)");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ error = slurm_db_add_prefix_assertion(&result);
+ if (error)
+ goto release_comment;
+
+ return 0;
+
+ release_comment:
+ free((void *)result.comment);
+ return error;
+ }
+
+ static int
+ load_prefix_array(json_t *array, bool is_assertion)
+ {
+ json_t *element;
+ int index, error;
+
+ json_array_foreach(array, index, element) {
+ error = load_single_prefix(element, is_assertion);
+ if (error) {
+ if (error == -EEXIST)
+ warnx(
+ "The prefix %s element #%d, is duplicated or covered by another %s; SLURM loading will be stopped",
+ (is_assertion ? "assertion" : "filter"),
+ index + 1,
+ (is_assertion ? "assertion" : "filter"));
+ else
+ warnx(
+ "Error at prefix %s, element #%d, SLURM loading will be stopped",
+ (is_assertion ? "assertions" : "filters"),
+ index + 1);
+
+ return error;
+ }
+ }
+
+ return 0;
+ }
+
+ static void
+ init_slurm_bgpsec(struct slurm_bgpsec *slurm_bgpsec)
+ {
+ slurm_bgpsec->data_flag = SLURM_COM_FLAG_NONE;
+ slurm_bgpsec->asn = 0;
+ slurm_bgpsec->ski = NULL;
+ slurm_bgpsec->ski_len = 0;
+ slurm_bgpsec->router_public_key = NULL;
+ slurm_bgpsec->router_public_key_len = 0;
+ slurm_bgpsec->comment = NULL;
+ }
+
+ static int
+ load_single_bgpsec(json_t *object, bool is_assertion)
+ {
+ struct slurm_bgpsec result;
+ size_t member_count;
+ int error;
+
+ if (!json_is_object(object)) {
+ warnx("Not a valid JSON object");
+ return -EINVAL;
+ }
+
+ init_slurm_bgpsec(&result);
+ member_count = 0;
+
+ error = set_asn(object, is_assertion, &result.asn, &result.data_flag,
+ &member_count);
+ if (error)
+ return error;
+
+ error = set_ski(object, is_assertion, &result, &member_count);
+ if (error)
+ return error;
+
+ error = set_router_pub_key(object, is_assertion, &result,
+ &member_count);
+ if (error)
+ goto release_ski;
+
+ error = set_comment(object, &result.comment, &result.data_flag,
+ &member_count);
+ if (error)
+ goto release_router_key;
+
+ /* A single comment isn't valid */
+ if (result.data_flag == SLURM_COM_FLAG_COMMENT) {
+ warnx("Single comments aren't valid");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ /* A filter must have ASN and/or SKI */
+ if (!is_assertion) {
+ if ((result.data_flag &
+ (SLURM_COM_FLAG_ASN | SLURM_BGPS_FLAG_SKI)) == 0) {
+ warnx("BGPsec filter must have an asn and/or SKI");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ /* Validate expected members */
+ if (!valid_members_count(object, member_count)) {
+ warnx("BGPsec filter has unknown members (see RFC 8416 section 3.3.2)");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ error = slurm_db_add_bgpsec_filter(&result);
+ if (error)
+ goto release_comment;
+
+ return 0;
+ }
+
+ /* Validate expected members */
+ if (!valid_members_count(object, member_count)) {
+ warnx("BGPsec assertion has unknown members (see RFC 8416 section 3.4.2)");
+ error = -EINVAL;
+ goto release_comment;
+ }
+
+ error = slurm_db_add_bgpsec_assertion(&result);
+ if (error)
+ goto release_comment;
+
+ return 0;
+
+ release_comment:
+ free((void *)result.comment);
+ release_router_key:
+ free(result.router_public_key);
+ release_ski:
+ free(result.ski);
+ return error;
+ }
+
+ static int
+ load_bgpsec_array(json_t *array, bool is_assertion)
+ {
+ json_t *element;
+ int index, error;
+
+ json_array_foreach(array, index, element) {
+ error = load_single_bgpsec(element, is_assertion);
+ if (error) {
+ if (error == -EEXIST)
+ warnx(
+ "The bgpsec %s element #%d, is duplicated or covered by another %s; SLURM loading will be stopped",
+ (is_assertion ? "assertion" : "filter"),
+ index + 1,
+ (is_assertion ? "assertion" : "filter"));
+ else
+ warnx(
+ "Error at bgpsec %s, element #%d, SLURM loading will be stopped",
+ (is_assertion ? "assertions" : "filters"),
+ index + 1);
+
+ return error;
+ }
+ }
+
+ return 0;
+ }
+
+ static int
+ load_version(json_t *root)
+ {
+ json_int_t version;
+ int error;
+
+ version = -1;
+ error = json_get_int(root, SLURM_VERSION, &version);
+ if (error)
+ return error;
+
+ /* Validate data */
+ if (version != 1) {
+ warnx("'%s' must be 1", SLURM_VERSION);
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ static int
+ load_filters(json_t *root)
+ {
+ json_t *filters, *prefix, *bgpsec;
+ size_t expected_members;
+ int error;
+
+ filters = json_get_object(root, VALIDATION_OUTPUT_FILTERS);
+ CHECK_REQUIRED(filters, VALIDATION_OUTPUT_FILTERS)
+
+ prefix = json_get_array(filters, PREFIX_FILTERS);
+ CHECK_REQUIRED(prefix, PREFIX_FILTERS)
+
+ bgpsec = json_get_array(filters, BGPSEC_FILTERS);
+ CHECK_REQUIRED(bgpsec, BGPSEC_FILTERS)
+
+ expected_members = 2;
+ if (!valid_members_count(filters, expected_members)) {
+ warnx(
+ "SLURM '%s' must contain only %lu members (RFC 8416 section 3.2)",
+ VALIDATION_OUTPUT_FILTERS,
+ expected_members);
+ return -EINVAL;
+ }
+
+ /* Arrays loaded, now iterate */
+ error = load_prefix_array(prefix, false);
+ if (error)
+ return error;
+
+ error = load_bgpsec_array(bgpsec, false);
+ if (error)
+ return error;
+
+ return 0;
+ }
+
+ static int
+ load_assertions(json_t *root)
+ {
+ json_t *assertions, *prefix, *bgpsec;
+ size_t expected_members;
+ int error;
+
+ assertions = json_get_object(root, LOCALLY_ADDED_ASSERTIONS);
+ CHECK_REQUIRED(assertions, LOCALLY_ADDED_ASSERTIONS)
+
+ prefix = json_get_array(assertions, PREFIX_ASSERTIONS);
+ CHECK_REQUIRED(prefix, PREFIX_ASSERTIONS)
+
+ bgpsec = json_get_array(assertions, BGPSEC_ASSERTIONS);
+ CHECK_REQUIRED(bgpsec, BGPSEC_ASSERTIONS)
+
+ expected_members = 2;
+ if (!valid_members_count(assertions, expected_members)) {
+ warnx(
+ "SLURM '%s' must contain only %lu members (RFC 8416 section 3.2)",
+ LOCALLY_ADDED_ASSERTIONS,
+ expected_members);
+ return -EINVAL;
+ }
+
+ error = load_prefix_array(prefix, true);
+ if (error)
+ return error;
+
+ error = load_bgpsec_array(bgpsec, true);
+ if (error)
+ return error;
+
+ return 0;
+ }
+
+ static int
+ handle_json(json_t *root)
+ {
+ size_t expected_members;
+ int error;
+
+ if (!json_is_object(root)) {
+ warnx("The root of the SLURM is not a JSON object.");
+ return -EINVAL;
+ }
+
+ error = load_version(root);
+ if (error)
+ return error;
+
+ error = load_filters(root);
+ if (error)
+ return error;
+
+ error = load_assertions(root);
+ if (error)
+ return error;
+
+ expected_members = 3;
+ if (!valid_members_count(root, expected_members)) {
+ warnx(
+ "SLURM root must have only %lu members (RFC 8416 section 3.2)",
+ expected_members);
+ return -EINVAL;
+ }
+
+ return 0;
+ }