+++ /dev/null
----
-title: Incidence
----
-
-[Documentation](index.html) > {{ page.title }}
-
-# {{ page.title }}
-
-## Index
-
-1. [Introduction](#introduction)
-2. [`incidences` definition](#incidences-definition)
-3. [Incidence types](#incidence-types)
- 1. [rsaEncryption signature algorithm has parameters](#rsaencryption-signature-algorithm-has-parameters)
- 2. [certificate public key algorithm is rsaEncryption](#certificate-public-key-algorithm-is-rsaencryption)
-
-## Introduction
-
-The RPKI RFCs define fairly strict profiles for RPKI objects, and are unequivocal in stating that incorrectly-formed objects are supposed to be rejected by Relying Party validation. In practice, however, this does not stop a significant amount of Certificate Authorities from issuing incorrect objects.
-
-By default, Fort is as pedantic as it can reasonably be. The `incidence` section of its configuration file is a means to modify its behavior upon encountering profile violations that, from experience, are often overlooked.
-
-## `incidences` definition
-
-`incidences` is a JSON array that contains (anonymous) incidence elements. Here's an example:
-
-```
-"incidences": [
- {
- "name": "rsaEncryption signature algorithm has parameters",
- "action": "warn"
- }, {
- "name": "certificate public key algorithm is rsaEncryption",
- "action": "ignore"
- }
-]
-```
-
-`name` is the identifier of an incidence. It's case-sensitive and developer-defined. It states the particular error condition that will be handled by the remaining field.
-
-`action` is an enumeration that states the outcome of a violation of the corresponding incidence. It can take one of three values:
-
-1. `error`: Print error message in `error` log level, fail validation of the offending object (and all of its children).
-2. `warn`: Print error message in `warning` log level, continue validation as if nothing happened.
-3. `ignore`: Do not print error message, continue validation as if nothing happened.
-
-By Fort's pedantic nature, most incidences have an `action` of `error` by default.
-
-## Incidence types
-
-Presently, there are only two incidence types defined. This list might grow over time, depending on the state of the global RPKI and user demand.
-
-### rsaEncryption signature algorithm has parameters
-
-[RFC 6488](https://tools.ietf.org/html/rfc6488) (RPKI Signed Objects) defers signature algorithm specification to RFC 6485:
-
-```
-2.1.6.5. signatureAlgorithm
-
- The signatureAlgorithm MUST conform to the RPKI Algorithms and Key
- Size Profile specification [RFC6485].
-```
-
-[6485](https://tools.ietf.org/html/rfc6485) has been obsoleted by [7935](https://tools.ietf.org/html/rfc7935), which states the following:
-
-```
- RPKI implementations MUST
- accept either rsaEncryption or sha256WithRSAEncryption for the
- SignerInfo signatureAlgorithm field when verifying CMS SignedData
- objects (for compatibility with objects produced by implementations
- conforming to [RFC6485]).
-```
-
-Regarding `rsaEncryption`, [3370](https://tools.ietf.org/html/rfc3370) states
-
-```
- When the rsaEncryption algorithm identifier is used, the
- AlgorithmIdentifier parameters field MUST contain NULL.
-```
-
-As of 2019-05-21, many signed objects in the global RPKI break this rule. (`parameters` is often defined as an empty object, but not NULL nonetheless.)
-
-If not `ignore`d, Fort will report this incidence with the following error message:
-
-```
-<log level>: <offending file name>: rsaEncryption signature algorithm has parameters.
-```
-
-### certificate public key algorithm is rsaEncryption
-
-> TODO missing code
4. [Running Fort](run.html)
5. [Fort usage](usage.html)
6. [SLURM](slurm.html)
-7. [Incidences](incidence.html)
17. [`rsync.program`](#rsyncprogram)
18. [`rsync.arguments-recursive`](#rsyncarguments-recursive)
19. [`rsync.arguments-flat`](#rsyncarguments-flat)
- 20. [`incidences`](#incidences)
## Syntax
"$REMOTE",
"$LOCAL"
]
- },
-
- "<a href="#incidences">incidences</a>": [
- {
- "name": "rsaEncryption signature algorithm has parameters",
- "action": "ignore"
- }
- ]
+ }
}
</code></pre>
Arguments needed by [`rsync.program`](#rsyncprogram) to perform a single-file rsync.
Fort will replace `"$REMOTE"` with the remote URL it needs to download, and `"$LOCAL"` with the target local directory where the file is supposed to be dropped.
-
-### `incidences`
-
-- **Type:** JSON Object
-- **Availability:** JSON only
-
-A listing of actions to be performed by validation upon encountering certain error conditions. See [incidence](incidence.html).
fort_SOURCES += config/boolean.c config/boolean.h
fort_SOURCES += config/filename_format.h config/filename_format.c
-fort_SOURCES += config/incidences.h config/incidences.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 += data_structure/uthash.h
fort_SOURCES += data_structure/uthash_nonfatal.h
-fort_SOURCES += incidence/incidence.h incidence/incidence.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
#include "algorithm.h"
+#include <stdbool.h>
#include <openssl/obj_mac.h>
#include "log.h"
validate_certificate_public_key_algorithm(int nid)
{
/*
- * TODO (whatever) RFC says sha256WithRSAEncryption, but everyone uses
- * rsaEncryption.
+ * RFC says sha256WithRSAEncryption, but current IETF concensus (and
+ * practice) say that the right one is rsaEncryption.
+ * https://mailarchive.ietf.org/arch/browse/sidr/
*/
- if (nid == NID_rsaEncryption || nid == NID_sha256WithRSAEncryption)
+ if (nid == NID_rsaEncryption)
return 0;
- return pr_err("Certificate's public key format is NID '%d', not RSA nor RSA+SHA256.",
+ return pr_err("Certificate's public key format is NID '%d', not rsaEncryption.",
nid);
}
return pr_err("The hash algorithm of the '%s' is not SHA256.", what);
}
+static int
+is_asn1_null(ANY_t *any)
+{
+ if (any == NULL)
+ return true;
+
+ if (any->size != 2)
+ return false;
+ if (any->buf[0] != 5 || any->buf[1] != 0)
+ return false;
+
+ return true;
+}
+
int
validate_cms_signature_algorithm(AlgorithmIdentifier_t *id)
{
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
};
uint8_t last;
- int error;
if (id == NULL)
return pr_err("The signature algorithm is absent.");
goto incorrect_oid;
/*
+ * This one's a royal mess.
+ *
* RFC 7935:
* The object identifier and parameters for rsaEncryption
* [RFC3370] MUST be used for the SignerInfo signatureAlgorithm field
* When any of these four object identifiers appears within an
* AlgorithmIdentifier, the parameters MUST be NULL. Implementations
* MUST accept the parameters being absent as well as present.
+ *
+ * I don't know if "MUST contain NULL" means that it must be absent,
+ * or whether it must contain a NULL ASN object. Everyone is doing the
+ * latter, which you think would be the logical option, but then 3370
+ * throws this curveball at us:
+ *
+ * There are two possible encodings for the SHA-1 AlgorithmIdentifier
+ * parameters field. The two alternatives arise from the fact that when
+ * the 1988 syntax for AlgorithmIdentifier was translated into the 1997
+ * syntax, the OPTIONAL associated with the AlgorithmIdentifier
+ * parameters got lost. Later the OPTIONAL was recovered via a defect
+ * report, but by then many people thought that algorithm parameters
+ * were mandatory. Because of this history some implementations encode
+ * parameters as a NULL element and others omit them entirely. The
+ * correct encoding is to omit the parameters field; however,
+ * implementations MUST also handle a SHA-1 AlgorithmIdentifier
+ * parameters field which contains a NULL.
+ *
+ * It does seem to be talking about SHA-1 only, but it's just not clear,
+ * because it doesn't care to ellaborate in the rsaEncryption section at
+ * all. Even though it's aware of the ambiguity, it just says "MUST
+ * contain NULL." It doesn't say "MUST be empty" or "MUST contain a NULL
+ * object."
+ *
+ * I really don't think this is worth making a fuss over, so we'll just
+ * accept both.
*/
- if (last == 1 && id->parameters != NULL) {
- error = incidence(INID_RSAENCRYPTION_SIGNALG_HAS_PARAMS,
- "rsaEncryption signature algorithm has parameters.");
- if (error)
- return error;
- }
+ if (id->parameters != NULL && !is_asn1_null(id->parameters))
+ return pr_err("The signature algorithm has parameters.");
return 0;
#include "json_handler.h"
#include "log.h"
#include "config/boolean.h"
-#include "config/incidences.h"
#include "config/str.h"
#include "config/uint.h"
#include "config/uint32.h"
.doc = "File name variant to print during debug/error messages",
},
- /* Incidences */
- {
- .id = 4001,
- .name = "incidences",
- .type = >_incidences,
- .doc = "Override actions on validation errors",
- .availability = AVAILABILITY_JSON,
- },
-
{ 0 },
};
+++ /dev/null
-#include "config/incidences.h"
-
-#include <getopt.h>
-#include "incidence/incidence.h"
-
-static void
-incidences_print(struct option_field const *field, void *_value)
-{
- incidence_print();
-}
-
-static int
-incidences_parse_json(struct option_field const *opt, json_t *json,
- void *_result)
-{
- return incidence_update(json);
-}
-
-const struct global_type gt_incidences = {
- .print = incidences_print,
- .parse.json = incidences_parse_json,
-};
+++ /dev/null
-#ifndef SRC_CONFIG_INCIDENCES_H_
-#define SRC_CONFIG_INCIDENCES_H_
-
-#include "config/types.h"
-
-extern const struct global_type gt_incidences;
-
-#endif /* SRC_CONFIG_INCIDENCES_H_ */
+++ /dev/null
-#include "incidence/incidence.h"
-
-#include <assert.h>
-#include <stdbool.h>
-#include <string.h>
-#include "common.h"
-#include "json_parser.h"
-#include "log.h"
-#include "data_structure/common.h"
-
-struct incidence {
- const enum incidence_id id;
- char const *const name;
- const enum incidence_action default_action;
- enum incidence_action action;
-};
-
-static struct incidence incidences[__INID_MAX] = {
- {
- INID_RSAENCRYPTION_SIGNALG_HAS_PARAMS,
- "rsaEncryption signature algorithm has parameters",
- INAC_ERROR,
- },
-};
-
-static int
-name2id(char const *name, enum incidence_id *id)
-{
- array_index i;
-
- for (i = 0; i < __INID_MAX; i++) {
- if (strcmp(name, incidences[i].name) == 0) {
- *id = i;
- return 0;
- }
- }
-
- return pr_err("Unknown incidence name: %s", name);
-}
-
-static char const *
-action2str(enum incidence_action action)
-{
- switch (action) {
- case INAC_IGNORE:
- return "ignore";
- case INAC_WARN:
- return "warn";
- case INAC_ERROR:
- return "error";
- }
-
- return "unknown";
-}
-
-static int
-init_action(json_t *json)
-{
- enum incidence_id id;
- char const *name;
- char const *action_str;
- enum incidence_action action;
- int error;
-
- error = json_get_string(json, "name", &name);
- if (error)
- return error;
- error = name2id(name, &id);
- if (error)
- return error;
- error = json_get_string(json, "action", &action_str);
- if (error)
- return error;
-
- if (strcmp("ignore", action_str) == 0)
- action = INAC_IGNORE;
- else if (strcmp("warn", action_str) == 0)
- action = INAC_WARN;
- else if (strcmp("error", action_str) == 0)
- action = INAC_ERROR;
- else
- return pr_err("Unknown incidence action: '%s'", action_str);
-
- if (action > incidences[id].action)
- return pr_err("The '%s' incidence cannot have a more severe action than '%s'.",
- name, action2str(incidences[id].action));
-
- incidences[id].action = action;
- return 0;
-}
-
-/**
- * Concurrent inits are allowed.
- */
-int
-incidence_init(void)
-{
- array_index i;
-
- /* Make sure the programmer didn't desync the id enum and the array. */
- assert(__INID_MAX == ARRAY_LEN(incidences));
- for (i = 0; i < __INID_MAX; i++) {
- assert(i == incidences[i].id);
- /* Also init. */
- incidences[i].action = incidences[i].default_action;
- }
-
- return 0;
-}
-
-/**
- * Concurrent calls to this function are allowed.
- */
-int
-incidence_update(json_t *json)
-{
- array_index i;
- json_t *child;
- int error;
-
- if (!json_is_array(json))
- return pr_err("The incidences JSON element is supposed to be an array.");
-
- json_array_foreach(json, i, child) {
- error = init_action(child);
- if (error)
- return error;
- }
-
- return 0;
-}
-
-void
-incidence_print(void)
-{
- array_index i;
- bool printed;
-
- pr_info("Custom incidences:");
- pr_indent_add();
-
- printed = false;
-
- for (i = 0; i < __INID_MAX; i++) {
- if (incidences[i].action != incidences[i].default_action) {
- pr_info("%s: %s", incidences[i].name,
- action2str(incidences[i].action));
- printed = true;
- }
- }
-
- if (!printed)
- pr_info("<None>");
-
- pr_indent_rm();
-}
-
-enum incidence_action
-incidence_get_action(enum incidence_id id)
-{
- return incidences[id].action;
-}
+++ /dev/null
-#ifndef SRC_INCIDENCE_INCIDENCE_H_
-#define SRC_INCIDENCE_INCIDENCE_H_
-
-#include <jansson.h>
-
-/*
- * Note: If you need to add, modify or delete an element from this enum,
- * remember that you also need to add it to the incidences array. That's all.
- */
-enum incidence_id {
- INID_RSAENCRYPTION_SIGNALG_HAS_PARAMS,
-
- __INID_MAX,
-};
-
-enum incidence_action {
- /**
- * Do not print error message, continue validation as if nothing
- * happened.
- */
- INAC_IGNORE,
- /**
- * Print error message in warning log level, continue validation as if
- * nothing happened.
- */
- INAC_WARN,
- /**
- * Print error message in error log level, fail validation of the
- * offending object (and all of its children).
- */
- INAC_ERROR,
-};
-
-int incidence_init(void); /* incidence_destroy() is not needed. */
-int incidence_update(json_t *);
-
-void incidence_print(void);
-enum incidence_action incidence_get_action(enum incidence_id);
-
-#endif /* SRC_INCIDENCE_INCIDENCE_H_ */
PR_SUFFIX(STDERR);
return -EINVAL;
}
-
-/**
- * Prints the [format, ...] error message using the configured logging severity
- * of the @id incidence.
- */
-int
-incidence(enum incidence_id id, const char *format, ...)
-{
- enum incidence_action action;
- va_list args;
-
- action = incidence_get_action(id);
- switch (action) {
- case INAC_IGNORE:
- return 0;
- case INAC_WARN:
- PR_PREFIX(STDERR, COLOR_WARNING, "WRN", args);
- PR_SUFFIX(STDERR);
- return 0;
- case INAC_ERROR:
- PR_PREFIX(STDERR, COLOR_ERROR, "ERR", args);
- PR_SUFFIX(STDERR);
- return -EINVAL;
- }
-
- return pr_crit("Unknown incidence action: %u", action);
-}
#ifndef SRC_LOG_H_
#define SRC_LOG_H_
-#include "incidence/incidence.h"
-
/*
* I know that the OpenBSD style guide says that we shouldn't declare our own
* error printing functions, but we kind of need to do it:
int pr_enomem(void);
int pr_crit(const char *, ...) CHECK_FORMAT(1, 2);
-int incidence(enum incidence_id, const char *, ...) CHECK_FORMAT(2, 3);
-
#define PR_DEBUG printf("%s:%d (%s())\n", __FILE__, __LINE__, __func__)
#define PR_DEBUG_MSG(msg, ...) printf("%s:%d (%s()): " msg "\n", \
__FILE__, __LINE__, __func__, ##__VA_ARGS__)
print_stack_trace_on_segfault();
error = thvar_init();
- if (error)
- return error;
- error = incidence_init();
if (error)
return error;
int ok;
int error;
+ /* Reminder: X509_PUBKEY is the same as SubjectPublicKeyInfo. */
pubkey = X509_get_X509_PUBKEY(cert);
if (pubkey == NULL)
return crypto_err("X509_get_X509_PUBKEY() returned NULL");