]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Certificate: Remove subject name uniqueness validation
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 22 Nov 2022 18:14:34 +0000 (12:14 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 22 Nov 2022 18:17:41 +0000 (12:17 -0600)
RFC 6487:

> An issuer SHOULD use a different subject name if the subject's key
> pair has changed (i.e., when the CA issues a certificate as part of
> re-keying the subject.)

Fort's implementation was problematic. The code was comparing the
certificate's subject name and public key to siblings that had
potentially not been validated yet. It seems to me this would make it
possible for attackers to crash FORT (by posting invalid objects) or to
invalidate legitimate objects (by publishing siblings that contained
conflicting subject names and public keys, without having to worry about
the rest of the fields).

This would be somewhat difficult o fix. I asked on the mailing list and
Discord ("RPKI Community"), and it seems the concensus is "don't
validate it." Subject Names don't really matter that much, because
RPKI's primary concern is resource ownership, not identity. Furthermore,
I'm not convinced that chopping off branches off the tree because of a
clumsy key rollover is a good idea.

https://mailarchive.ietf.org/arch/msg/sidrops/mXWbCwh6RO8pAtt7N30Q9m6jUws/

Manually cherry-picked from 0a6a80b558e12304ba0e68c021848e292bfe3ce6.

Hopefully f1xes #86.

.gitignore
src/cert_stack.c
src/object/certificate.c
src/slurm/slurm_parser.c
test/rsync_test.c
test/rtr/primitive_reader_test.c

index 584ce7fb541a07e453db56ae0baf8691e89d6432..aa513484edeb8acb8d64798cbacaaa9efcefff0a 100644 (file)
@@ -76,6 +76,8 @@ install-sh
 missing
 src/configure_ac.h
 src/stamp-h1
+config.guess
+config.sub
 
 # Packaging
 *.7z
index 447895860fce36fa873069c9de10702a34f1d1b1..ed5e90eee17d96ff0638ce471c8eedfc631ce77b 100644 (file)
@@ -35,13 +35,6 @@ struct serial_number {
 
 STATIC_ARRAY_LIST(serial_numbers, struct serial_number)
 
-struct subject_name {
-       struct rfc5280_name *name;
-       char *file; /* File where this subject name was found. */
-};
-
-STATIC_ARRAY_LIST(subjects, struct subject_name)
-
 /**
  * Cached certificate data.
  */
@@ -54,7 +47,6 @@ struct metadata_node {
         * don't have many children, and I'm running out of time.
         */
        struct serial_numbers serials;
-       struct subjects subjects;
 
        /*
         * Certificate repository "level". This aims to identify if the
@@ -150,20 +142,12 @@ serial_cleanup(struct serial_number *serial)
        free(serial->file);
 }
 
-static void
-subject_cleanup(struct subject_name *subject)
-{
-       x509_name_put(subject->name);
-       free(subject->file);
-}
-
 static void
 meta_destroy(struct metadata_node *meta)
 {
        uri_refput(meta->uri);
        resources_destroy(meta->resources);
        serial_numbers_cleanup(&meta->serials, serial_cleanup);
-       subjects_cleanup(&meta->subjects, subject_cleanup);
        free(meta);
 }
 
@@ -356,11 +340,10 @@ x509stack_push(struct cert_stack *stack, struct rpki_uri *uri, X509 *x509,
        meta->uri = uri;
        uri_refget(uri);
        serial_numbers_init(&meta->serials);
-       subjects_init(&meta->subjects);
 
        error = init_resources(x509, policy, type, &meta->resources);
        if (error)
-               goto cleanup_subjects;
+               goto cleanup_serial;
 
        error = init_level(stack, &meta->level); /* Does not need a revert */
        if (error)
@@ -388,8 +371,7 @@ destroy_separator:
        free(defer_separator);
 destroy_resources:
        resources_destroy(meta->resources);
-cleanup_subjects:
-       subjects_cleanup(&meta->subjects, subject_cleanup);
+cleanup_serial:
        serial_numbers_cleanup(&meta->serials, serial_cleanup);
        uri_refput(meta->uri);
        free(meta);
@@ -448,14 +430,14 @@ x509stack_peek_level(struct cert_stack *stack)
 static int
 get_current_file_name(char **_result)
 {
-       char const *tmp;
+       char const *file_name;
        char *result;
 
-       tmp = fnstack_peek();
-       if (tmp == NULL)
+       file_name = fnstack_peek();
+       if (file_name == NULL)
                pr_crit("The file name stack is empty.");
 
-       result = strdup(tmp);
+       result = strdup(file_name);
        if (result == NULL)
                return pr_enomem();
 
@@ -532,108 +514,6 @@ x509stack_store_serial(struct cert_stack *stack, BIGNUM *number)
        return error;
 }
 
-/**
- * Intended to validate subject uniqueness.
- * "Stores" the subject in the current relevant certificate metadata, and
- * complains if there's a collision. The @cb should check the primary key of
- * the subject, it will be called when a subject isn't unique (certificate
- * shares the subject but not the public key). That's all.
- */
-int
-x509stack_store_subject(struct cert_stack *stack, struct rfc5280_name *subject,
-    subject_pk_check_cb cb, void *arg)
-{
-       struct metadata_node *meta;
-       struct subject_name *cursor;
-       array_index i;
-       struct subject_name duplicate;
-       bool duplicated;
-       int error;
-
-       /*
-        * There's something that's not clicking with me:
-        *
-        * "Each distinct subordinate CA and
-        * EE certified by the issuer MUST be identified using a subject name
-        * that is unique per issuer.  In this context, 'distinct' is defined as
-        * an entity and a given public key."
-        *
-        * Does the last sentence have any significance to us? I don't even
-        * understand why the requirement exists. 5280 and 6487 don't even
-        * define "entity." We'll take the closest definition from the context,
-        * specifically from RFC 6484 or RFC 6481 (both RFCs don't define
-        * "entity" explicitly, but use the word in a way that it can be
-        * inferred what it means).
-        *
-        * "An issuer SHOULD use a different
-        * subject name if the subject's key pair has changed (i.e., when the CA
-        * issues a certificate as part of re-keying the subject.)"
-        *
-        * It's really weird that it seems to be rewording the same requirement
-        * except the first version is defined as MUST and the second one is
-        * defined as SHOULD.
-        *
-        * Ugh. Okay. Let's use some common sense. There are four possible
-        * situations:
-        *
-        * - Certificates do not share name nor public key. We should accept
-        *   this.
-        * - Certificates share name, but not public key. We should reject this.
-        * - Certificates share public key, but not name. This is basically
-        *   impossible, but fine nonetheless. Accept.
-        *   (But maybe issue a warning. It sounds like the two children can
-        *   impersonate each other.)
-        * - Certificates share name and public key. This likely means that we
-        *   are looking at two different versions of the same certificate.
-        *   Accept. (see rfc6484#section-4.7.1 for an example)
-        *
-        */
-
-       meta = SLIST_FIRST(&stack->metas);
-       if (meta == NULL)
-               return 0; /* The TA lacks siblings, so subject is unique. */
-
-       /* See the large comment in certstack_x509_store_serial(). */
-       duplicated = false;
-       ARRAYLIST_FOREACH(&meta->subjects, cursor, i) {
-               if (x509_name_equals(cursor->name, subject)) {
-                       error = cb(&duplicated, cursor->file, arg);
-                       if (error)
-                               return error;
-
-                       if (!duplicated)
-                               continue;
-
-                       char const *serial = x509_name_serialNumber(subject);
-                       pr_val_warn("Subject name '%s%s%s' is not unique. (Also found in '%s'.)",
-                           x509_name_commonName(subject),
-                           (serial != NULL) ? "/" : "",
-                           (serial != NULL) ? serial : "",
-                           cursor->file);
-                       return 0;
-               }
-       }
-
-       duplicate.name = subject;
-       x509_name_get(subject);
-
-       error = get_current_file_name(&duplicate.file);
-       if (error)
-               goto revert_name;
-
-       error = subjects_add(&meta->subjects, &duplicate);
-       if (error)
-               goto revert_file;
-
-       return 0;
-
-revert_file:
-       free(duplicate.file);
-revert_name:
-       x509_name_put(subject);
-       return error;
-}
-
 STACK_OF(X509) *
 certstack_get_x509s(struct cert_stack *stack)
 {
index b281ffed7e6b148d0eb9daa77635f7bcf68fc610..8966741d4e435c1a207685d4b75713ec802e079a 100644 (file)
@@ -185,126 +185,11 @@ spki_cmp(X509_PUBKEY *tal_spki, X509_PUBKEY *cert_spki,
        return 0;
 }
 
-/** Call whenever the public key is different and the subject is the same**/
-static int
-cert_different_pk_err(void)
-{
-       return 1;
-}
-
 /*
- * Beware: this function skips a lot (maybe all) RPKI validations for a signed
- * object, and clearly expects that the signed object has at least one
- * certificate.
- *
- * Allocates @result, don't forget to release it.
- *
- * It has been placed here to minimize the risk of misuse.
+ * https://mailarchive.ietf.org/arch/msg/sidrops/mXWbCwh6RO8pAtt7N30Q9m6jUws/
+ * Concensus (in mailing list as well as Discord) seems to be "do not check
+ * subject name uniqueness."
  */
-static int
-cert_from_signed_object(struct rpki_uri *uri, uint8_t **result, int *size)
-{
-       struct signed_object sobj;
-       ANY_t *cert;
-       unsigned char *tmp;
-       int error;
-
-       error = signed_object_decode(&sobj, uri);
-       if (error)
-               return error;
-
-       cert = sobj.sdata.decoded->certificates->list.array[0];
-
-       tmp = malloc(cert->size);
-       if (tmp == NULL) {
-               signed_object_cleanup(&sobj);
-               return pr_enomem();
-       }
-
-       memcpy(tmp, cert->buf, cert->size);
-
-       *result = tmp;
-       (*size) = cert->size;
-
-       signed_object_cleanup(&sobj);
-       return 0;
-}
-
-static int
-check_dup_public_key(bool *duplicated, char const *file, void *arg)
-{
-       X509 *curr_cert = arg; /* Current cert */
-       X509 *rcvd_cert;
-       X509_PUBKEY *curr_pk, *rcvd_pk;
-       struct rpki_uri *uri;
-       uint8_t *tmp;
-       int tmp_size;
-       int error;
-
-       uri = NULL;
-       rcvd_cert = NULL;
-       tmp_size = 0;
-
-       error = uri_create_rsync_str(&uri, file, strlen(file));
-       if (error)
-               return error;
-
-       /* Check if it's '.cer', otherwise treat as a signed object */
-       if (uri_is_certificate(uri)) {
-               error = certificate_load(uri, &rcvd_cert);
-               if (error)
-                       goto free_uri;
-       } else {
-               error = cert_from_signed_object(uri, &tmp, &tmp_size);
-               if (error)
-                       goto free_uri;
-
-               rcvd_cert = d2i_X509(NULL, (const unsigned char **) &tmp,
-                   tmp_size);
-               free(tmp); /* Release at once */
-               if (rcvd_cert == NULL) {
-                       error = val_crypto_err("Signed object's '%s' 'certificate' element does not decode into a Certificate",
-                           uri_val_get_printable(uri));
-                       goto free_uri;
-               }
-       }
-
-       curr_pk = X509_get_X509_PUBKEY(curr_cert);
-       if (curr_pk == NULL) {
-               error = val_crypto_err("X509_get_X509_PUBKEY() returned NULL");
-               goto free_cert;
-       }
-
-       rcvd_pk = X509_get_X509_PUBKEY(rcvd_cert);
-       if (rcvd_pk == NULL) {
-               error = val_crypto_err("X509_get_X509_PUBKEY() returned NULL");
-               goto free_cert;
-       }
-
-       /*
-        * The function response will be:
-        *   < 0 if there's an error
-        *   = 0 if the PKs are equal
-        *   > 0 if the PKs are different
-        */
-       error = spki_cmp(curr_pk, rcvd_pk, cert_different_pk_err,
-           cert_different_pk_err);
-
-       if (error < 0)
-               goto free_cert;
-
-       /* No error, a positive value means the name is duplicated */
-       if (error)
-               (*duplicated) = true;
-       error = 0;
-
-free_cert:
-       X509_free(rcvd_cert);
-free_uri:
-       uri_refput(uri);
-       return error;
-}
-
 static int
 validate_subject(X509 *cert)
 {
@@ -321,9 +206,6 @@ validate_subject(X509 *cert)
                return error;
        pr_val_debug("Subject: %s", x509_name_commonName(name));
 
-       error = x509stack_store_subject(validation_certstack(state), name,
-           check_dup_public_key, cert);
-
        x509_name_put(name);
        return error;
 }
index 00c25b988b5dc05844928a701471178a4c13a089..689f513d0aeb619e6c05d36d7e157a08624cc92a 100644 (file)
@@ -71,6 +71,7 @@ set_asn(json_t *object, bool is_assertion, uint32_t *result, uint8_t *flag,
        json_int_t int_tmp;
        int error;
 
+       int_tmp = 0;
        error = json_get_int(object, ASN, &int_tmp);
        if (error == -ENOENT) {
                if (is_assertion)
@@ -182,6 +183,7 @@ set_max_prefix_length(json_t *object, bool is_assertion, uint8_t addr_fam,
        json_int_t int_tmp;
        int error;
 
+       int_tmp = 0;
        error = json_get_int(object, MAX_PREFIX_LENGTH, &int_tmp);
        if (error == -ENOENT)
                return 0; /* Optional for assertions, unsupported by filters */
index ad4a9efe85d212f084ddcd192fe5ae97b8176dd5..7b99a961b57211fcd8b9c26bc3ffc0fdcbdb2b84 100644 (file)
@@ -75,6 +75,7 @@ START_TEST(rsync_test_list)
 {
        struct uri_list *visited_uris;
 
+       visited_uris = NULL;
        ck_assert_int_eq(rsync_create(&visited_uris), 0);
 
        __mark_as_downloaded("rsync://example.foo/repository/", visited_uris);
index 969e771200ae1d249db7e0fdca47cc0143a020a3..779c12e513e47755320dc4b723cb0ac4604e0b85 100644 (file)
@@ -101,7 +101,7 @@ validate_massive_string(uint32_t expected_len, rtr_char *str)
        actual_len = strlen(str);
        if (expected_len != actual_len) {
                free(str);
-               ck_abort_msg("Expected length %zu != Actual length %zu",
+               ck_abort_msg("Expected length %u != Actual length %zu",
                    expected_len, actual_len);
        }