From: Alberto Leiva Popper Date: Wed, 6 Mar 2019 21:56:41 +0000 (-0600) Subject: Implement thisUpdate, nextUpdate and --roa-output-file X-Git-Tag: v0.0.2~75 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=991acc69bd87166af52748b5cdd4d5c5ad6ba306;p=thirdparty%2FFORT-validator.git Implement thisUpdate, nextUpdate and --roa-output-file - 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) --- diff --git a/man/rpki-validator.8 b/man/rpki-validator.8 index 9ff8eac9..c0c3f2e9 100644 --- a/man/rpki-validator.8 +++ b/man/rpki-validator.8 @@ -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 diff --git a/src/config.c b/src/config.c index e787d05e..3462739d 100644 --- a/src/config.c +++ b/src/config.c @@ -21,6 +21,11 @@ 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 = "", +}; + /** * 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 = >_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 = >_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 = >_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) { diff --git a/src/config.h b/src/config.h index 4243a0c7..a21e69de 100644 --- a/src/config.h +++ b/src/config.h @@ -6,6 +6,7 @@ #include #include #include +#include /** * 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); diff --git a/src/object/manifest.c b/src/object/manifest.c index 21211eb0..2060afc2 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -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) diff --git a/src/object/roa.c b/src/object/roa.c index 05f82d56..7ad3e730 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -4,6 +4,7 @@ #include #include +#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; }