]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Implement thisUpdate, nextUpdate and --roa-output-file
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 6 Mar 2019 21:56:41 +0000 (15:56 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 6 Mar 2019 21:59:42 +0000 (15:59 -0600)
- Implement manifest thisUpdate and nextUpdate validation
- Print dates during manifest date errors
- ROA file output target is now configurable
  (Used to be dumped into stdout, which was annoying because it was
  mixed with the INFO logging)

man/rpki-validator.8
src/config.c
src/config.h
src/object/manifest.c
src/object/roa.c

index 9ff8eac9716dc401f1130f495a2893e226a8ff8b..c0c3f2e97382f56415876e0d25e6efa9f751ff4d 100644 (file)
@@ -121,7 +121,7 @@ Shuffle URIs in the TAL before accessing them.
 
 --color-output
 .RS 4
-Print ANSI color codes?
+Print ANSI color codes.
 .RE
 .P
 
index e787d05e600f446e55074a382c00352a4a6e620a..3462739d94eb886b14b551d4d13ac7853966b981 100644 (file)
                        if ((opt->availability == 0) ||         \
                            (opt->availability & type))
 
+struct config_out_file {
+       FILE *fd;
+       char *file_name;
+};
+
 /**
  * To add a member to this structure,
  *
@@ -59,13 +64,15 @@ struct rpki_config {
                bool color;
                /** Format in which file names will be printed. */
                enum filename_format filename_format;
+               /** Output stream where the valid ROAs will be dumped. */
+               struct config_out_file roa_output;
        } output;
 };
 
 static void print_usage(FILE *, bool);
 
 #define DECLARE_PRINT_FN(name)                                         \
-       void name(                                                      \
+       static void name(                                               \
            struct group_fields const *,                                \
            struct option_field const *,                                \
            void *                                                      \
@@ -76,6 +83,7 @@ DECLARE_PRINT_FN(print_string);
 DECLARE_PRINT_FN(print_string_array);
 DECLARE_PRINT_FN(print_sync_strategy);
 DECLARE_PRINT_FN(print_filename_format);
+DECLARE_PRINT_FN(print_out_file);
 
 #define DECLARE_PARSE_ARGV_FN(name)                                    \
        static int name(                                                \
@@ -88,6 +96,7 @@ DECLARE_PARSE_ARGV_FN(parse_argv_u_int);
 DECLARE_PARSE_ARGV_FN(parse_argv_string);
 DECLARE_PARSE_ARGV_FN(parse_argv_sync_strategy);
 DECLARE_PARSE_ARGV_FN(parse_argv_filename_format);
+DECLARE_PARSE_ARGV_FN(parse_argv_out_file);
 
 #define DECLARE_PARSE_TOML_FN(name)                                    \
        static int name(                                                \
@@ -101,6 +110,7 @@ DECLARE_PARSE_TOML_FN(parse_toml_string);
 DECLARE_PARSE_TOML_FN(parse_toml_sync_strategy);
 DECLARE_PARSE_TOML_FN(parse_toml_string_array);
 DECLARE_PARSE_TOML_FN(parse_toml_filename_format);
+DECLARE_PARSE_TOML_FN(parse_toml_out_file);
 
 #define DECLARE_HANDLE_FN(name)                                                \
        static int name(                                                \
@@ -115,6 +125,7 @@ DECLARE_HANDLE_FN(handle_toml);
 #define DECLARE_FREE_FN(name) static void name(void *)
 DECLARE_FREE_FN(free_string);
 DECLARE_FREE_FN(free_string_array);
+DECLARE_FREE_FN(free_out_file);
 
 static char const *program_name;
 static struct rpki_config rpki_config;
@@ -174,6 +185,16 @@ static const struct global_type gt_filename_format = {
        .arg_doc = FNF_VALUE_GLOBAL "|" FNF_VALUE_LOCAL "|" FNF_VALUE_NAME,
 };
 
+static const struct global_type gt_out_file = {
+       .has_arg = required_argument,
+       .size = sizeof(struct config_out_file),
+       .print = print_out_file,
+       .parse.argv = parse_argv_out_file,
+       .parse.toml = parse_toml_out_file,
+       .free = free_out_file,
+       .arg_doc = "<file>",
+};
+
 /**
  * An option that takes no arguments, is not correlated to any rpki_config
  * fields, and is entirely managed by its handler function.
@@ -286,13 +307,19 @@ static const struct option_field output_fields[] = {
                .name = "color-output",
                .type = &gt_bool,
                .offset = offsetof(struct rpki_config, output.color),
-               .doc = "Print ANSI color codes?",
+               .doc = "Print ANSI color codes.",
        }, {
                .id = 4000,
                .name = "output-file-name-format",
                .type = &gt_filename_format,
                .offset = offsetof(struct rpki_config, output.filename_format),
                .doc = "File name variant to print during debug/error messages",
+       }, {
+               .id = 'o',
+               .name = "roa-output-file",
+               .type = &gt_out_file,
+               .offset = offsetof(struct rpki_config, output.roa_output),
+               .doc = "File where the valid ROAs will be dumped.",
        },
        { 0 },
 };
@@ -331,7 +358,7 @@ get_rpki_config_field(struct option_field const *field)
        return ((unsigned char *) &rpki_config) + field->offset;
 }
 
-void
+static void
 print_bool(struct group_fields const *group, struct option_field const *field,
     void *_value)
 {
@@ -340,7 +367,7 @@ print_bool(struct group_fields const *group, struct option_field const *field,
            (*value) ? "true" : "false");
 }
 
-void
+static void
 print_u_int(struct group_fields const *group, struct option_field const *field,
     void *value)
 {
@@ -348,14 +375,14 @@ print_u_int(struct group_fields const *group, struct option_field const *field,
            *((unsigned int *) value));
 }
 
-void
+static void
 print_string(struct group_fields const *group, struct option_field const *field,
     void *value)
 {
        pr_info("%s.%s: %s", group->name, field->name, *((char **) value));
 }
 
-void
+static void
 print_string_array(struct group_fields const *group,
     struct option_field const *field, void *_value)
 {
@@ -373,7 +400,7 @@ print_string_array(struct group_fields const *group,
        pr_indent_rm();
 }
 
-void
+static void
 print_sync_strategy(struct group_fields const *group,
     struct option_field const *field, void *value)
 {
@@ -395,7 +422,7 @@ print_sync_strategy(struct group_fields const *group,
        pr_info("%s.%s: %s", group->name, field->name, str);
 }
 
-void
+static void
 print_filename_format(struct group_fields const *group,
     struct option_field const *field, void *value)
 {
@@ -417,6 +444,14 @@ print_filename_format(struct group_fields const *group,
        pr_info("%s.%s: %s", group->name, field->name, str);
 }
 
+static void
+print_out_file(struct group_fields const *group,
+    struct option_field const *field, void *value)
+{
+       struct config_out_file *file = value;
+       pr_info("%s.%s: %s", group->name, field->name, file->file_name);
+}
+
 static int
 parse_argv_bool(struct option_field const *field, char const *str, void *result)
 {
@@ -520,6 +555,28 @@ parse_argv_filename_format(struct option_field const *field, char const *str,
        return 0;
 }
 
+static int
+parse_argv_out_file(struct option_field const *field, char const *file_name,
+    void *_result)
+{
+       struct config_out_file *file = _result;
+
+       field->type->free(file);
+
+       file->file_name = strdup(file_name);
+       if (file->file_name == NULL)
+               return pr_enomem();
+
+       file->fd = fopen(file_name, "w");
+       if (file->fd == NULL) {
+               free(file->file_name);
+               file->file_name = NULL;
+               return pr_errno(errno, "Could not open file '%s'", file_name);
+       }
+
+       return 0;
+}
+
 static int
 parse_toml_bool(struct option_field const *opt, struct toml_table_t *toml,
     void *_result)
@@ -592,6 +649,7 @@ parse_toml_sync_strategy(struct option_field const *opt,
        int error;
        char *string;
 
+       string = NULL;
        error = parse_toml_string(opt, toml, &string);
        if (error)
                return error;
@@ -653,6 +711,7 @@ parse_toml_filename_format(struct option_field const *opt,
        int error;
        char *string;
 
+       string = NULL;
        error = parse_toml_string(opt, toml, &string);
        if (error)
                return error;
@@ -663,6 +722,24 @@ parse_toml_filename_format(struct option_field const *opt,
        return error;
 }
 
+static int
+parse_toml_out_file(struct option_field const *opt, struct toml_table_t *toml,
+    void *_result)
+{
+       char *file_name;
+       int error;
+
+       file_name = NULL;
+       error = parse_toml_string(opt, toml, &file_name);
+       if (error)
+               return error;
+
+       error = parse_argv_out_file(opt, file_name, _result);
+
+       free(file_name);
+       return error;
+}
+
 static int
 handle_help(struct option_field const *field, char *arg)
 {
@@ -706,11 +783,26 @@ free_string_array(void *_array)
 
        for (i = 0; i < array->length; i++)
                free(array->array[i]);
+       free(array->array);
 
        array->array = NULL;
        array->length = 0;
 }
 
+static void
+free_out_file(void *_file)
+{
+       struct config_out_file *file = _file;
+
+       if (file->fd != NULL) {
+               fclose(file->fd);
+               file->fd = NULL;
+       }
+
+       free(file->file_name);
+       file->file_name = NULL;
+}
+
 static bool
 is_alphanumeric(int chara)
 {
@@ -842,6 +934,8 @@ set_default_values(void)
 
        rpki_config.output.color = false;
        rpki_config.output.filename_format = FNF_GLOBAL;
+       rpki_config.output.roa_output.fd = NULL;
+       rpki_config.output.roa_output.file_name = NULL;
 
        return 0;
 
@@ -1019,6 +1113,14 @@ config_get_filename_format(void)
        return rpki_config.output.filename_format;
 }
 
+FILE *
+config_get_roa_output(void)
+{
+       return (rpki_config.output.roa_output.fd != NULL)
+           ? rpki_config.output.roa_output.fd
+           : stdout;
+}
+
 char *
 config_get_rsync_program(void)
 {
index 4243a0c7de9561dff9b0c451a3bdff8fae389bae..a21e69def85aceb7ca95c9b8269ab9b4efbd5ad9 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <toml.h>
+#include <openssl/bio.h>
 
 /**
  * Note: The only repository synchronization protocol implemented so far is
@@ -219,6 +220,7 @@ bool config_get_shuffle_uris(void);
 unsigned int config_get_max_cert_depth(void);
 bool config_get_color_output(void);
 enum filename_format config_get_filename_format(void);
+FILE *config_get_roa_output(void);
 char *config_get_rsync_program(void);
 struct string_array const *config_get_rsync_args(void);
 
index 21211eb0249c72b4195674c595fc6dfb08a701fa..2060afc2a13f7ddf04e917bd64aee81b4a22d9d9 100644 (file)
@@ -28,8 +28,53 @@ manifest_decode(OCTET_STRING_t *string, void *arg)
 static int
 validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next)
 {
-       const struct asn_TYPE_descriptor_s *def = &asn_DEF_GeneralizedTime;
-       return (GeneralizedTime_compare(def, this, next) < 0) ? 0 : -EINVAL;
+#define TM_FMT "%02d/%02d/%02d %02d:%02d:%02d"
+#define TM_ARGS(tm)                                                    \
+       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,                   \
+       tm.tm_hour, tm.tm_min, tm.tm_sec
+
+       time_t thisUpdate;
+       time_t nextUpdate;
+       time_t now;
+       struct tm thisUpdate_tm;
+       struct tm nextUpdate_tm;
+
+       /*
+        * BTW: We only need the tm variables for error messages, which are
+        * rarely needed.
+        * So maybe we could get a small performance boost by postponing the
+        * calls to localtime_r().
+        */
+       thisUpdate = asn_GT2time(this, &thisUpdate_tm, false);
+       nextUpdate = asn_GT2time(next, &nextUpdate_tm, false);
+
+       if (difftime(thisUpdate, nextUpdate) > 0) {
+               return pr_err(
+                   "Manifest's thisUpdate (" TM_FMT ") > nextUpdate ("
+                       TM_FMT ").",
+                   TM_ARGS(thisUpdate_tm),
+                   TM_ARGS(nextUpdate_tm));
+       }
+
+       now = time(NULL);
+       if (now == ((time_t) -1))
+               return pr_errno(errno, "Error getting the current time");
+
+       if (difftime(now, thisUpdate) < 0) {
+               return pr_err(
+                   "Manifest is not valid yet. (thisUpdate: " TM_FMT ")",
+                   TM_ARGS(thisUpdate_tm));
+       }
+       if (difftime(now, nextUpdate) > 0) {
+               return pr_err(
+                   "Manifest is expired. (nextUpdate: " TM_FMT ")",
+                   TM_ARGS(nextUpdate_tm));
+       }
+
+       return 0;
+
+#undef TM_FMT
+#undef TM_ARGS
 }
 
 static int
@@ -42,16 +87,20 @@ validate_manifest(struct Manifest *manifest)
        /* rfc6486#section-4.2.1 */
 
        /*
-        * TODO (field)
+        * BTW:
         *
-        * If a "one-time-use" EE certificate is employed to verify a manifest,
+        * "If a "one-time-use" EE certificate is employed to verify a manifest,
         * the EE certificate MUST have a validity period that coincides with
         * the interval from thisUpdate to nextUpdate, to prevent needless
-        * growth of the CA's CRL.
+        * growth of the CA's CRL."
         *
-        * If a "sequential-use" EE certificate is employed to verify a
+        * "If a "sequential-use" EE certificate is employed to verify a
         * manifest, the EE certificate's validity period needs to be no shorter
-        * than the nextUpdate time of the current manifest.
+        * than the nextUpdate time of the current manifest."
+        *
+        * It would appear that there's no way to tell whether an EE certificate
+        * is "one-time-use" or "sequential-use," so we have no way to validate
+        * this.
         */
 
        /* rfc6486#section-4.4.2 */
@@ -73,33 +122,6 @@ validate_manifest(struct Manifest *manifest)
        if (manifest->manifestNumber.size > 20)
                return pr_err("Manifest number is larger than 20 octets");
 
-       /*
-        * TODO (field)
-        *
-        * "CRL issuers conforming to this profile MUST encode thisUpdate as
-        * UTCTime for dates through the year 2049.  CRL issuers conforming to
-        * this profile MUST encode thisUpdate as GeneralizedTime for dates in
-        * the year 2050 or later. Conforming applications MUST be able to
-        * process dates that are encoded in either UTCTime or GeneralizedTime."
-        *
-        * WTF man. thisUpdate is defined in the spec as GeneralizedTime;
-        * not as CMSTime. This requirement makes no sense whatsoever.
-        *
-        * Check the errata?
-        */
-       /* manifest->thisUpdate */
-
-       /*
-        * TODO (field) again, same bullshit:
-        *
-        * "CRL issuers conforming to this profile MUST encode nextUpdate as
-        * UTCTime for dates through the year 2049.  CRL issuers conforming to
-        * this profile MUST encode nextUpdate as GeneralizedTime for dates in
-        * the year 2050 or later.  Conforming applications MUST be able to
-        * process dates that are encoded in either UTCTime or GeneralizedTime."
-        */
-       /* manifest->nextUpdate */
-
        /* rfc6486#section-4.4.3 */
        error = validate_dates(&manifest->thisUpdate, &manifest->nextUpdate);
        if (error)
index 05f82d565cc7e7f2fd21245d15c94bb5ce9e40a3..7ad3e73088c33aff3f8658399ec66dc4e5696f25 100644 (file)
@@ -4,6 +4,7 @@
 #include <arpa/inet.h>
 #include <libcmscodec/RouteOriginAttestation.h>
 
+#include "config.h"
 #include "log.h"
 #include "thread_var.h"
 #include "asn1/decode.h"
@@ -50,6 +51,9 @@ print_addr4(struct resources *parent, unsigned long asn,
                        return pr_err("Prefix length (%u) > maxLength (%lu)",
                            prefix.len, max_length);
                }
+
+       } else {
+               max_length = prefix.len;
        }
 
        str2 = inet_ntop(AF_INET, &prefix.addr, str, sizeof(str));
@@ -61,13 +65,8 @@ print_addr4(struct resources *parent, unsigned long asn,
                    prefix.len);
        }
 
-       printf("AS%lu,%s/%u", asn, str2, prefix.len);
-       if (roa_addr->maxLength != NULL)
-               printf(",%lu", max_length);
-       else
-               printf(",%u", prefix.len);
-       printf("\n");
-
+       fprintf(config_get_roa_output(), "AS%lu,%s/%u,%lu\n", asn, str2,
+           prefix.len, max_length);
        return 0;
 }
 
@@ -102,6 +101,9 @@ print_addr6(struct resources *parent, unsigned long asn,
                        return pr_err("Prefix length (%u) > maxLength (%lu)",
                            prefix.len, max_length);
                }
+
+       } else {
+               max_length = prefix.len;
        }
 
        str2 = inet_ntop(AF_INET6, &prefix.addr, str, sizeof(str));
@@ -113,13 +115,8 @@ print_addr6(struct resources *parent, unsigned long asn,
                    prefix.len);
        }
 
-       printf("AS%lu,%s/%u", asn, str2, prefix.len);
-       if (roa_addr->maxLength != NULL)
-               printf(",%lu", max_length);
-       else
-               printf(",%u", prefix.len);
-       printf("\n");
-
+       fprintf(config_get_roa_output(), "AS%lu,%s/%u,%lu\n", asn, str2,
+           prefix.len, max_length);
        return 0;
 }