]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Implement RFC 6493 (Ghostbusters record)
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 20 Feb 2019 19:12:46 +0000 (13:12 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 20 Feb 2019 19:12:46 +0000 (13:12 -0600)
Issue: I implemented 6493, but not 6350. Only the 6493-defined vCard
requirements are checked.

26 files changed:
src/Makefile.am
src/asn1/oid.h
src/common.c
src/common.h
src/log.c
src/object/certificate.c
src/object/crl.c
src/object/ghostbusters.c [new file with mode: 0644]
src/object/ghostbusters.h [new file with mode: 0644]
src/object/manifest.c
src/object/name.c [new file with mode: 0644]
src/object/name.h [new file with mode: 0644]
src/object/roa.c
src/object/signed_object.c
src/object/signed_object.h
src/object/vcard.c [new file with mode: 0644]
src/object/vcard.h [new file with mode: 0644]
src/rpp.c
src/rpp.h
src/rsync/rsync.c
src/state.c
test/Makefile.am
test/impersonator.c [new file with mode: 0644]
test/line_file_test.c
test/rsync_test.c
test/vcard_test.c [new file with mode: 0644]

index 41a34421df06dca80ef1b46ddff2e810b709a46e..09fa0613fbd03a25e2d37b9e593bd26b903c6ee1 100644 (file)
@@ -40,10 +40,13 @@ rpki_validator_SOURCES += crypto/hash.h crypto/hash.c
 
 rpki_validator_SOURCES += object/certificate.h object/certificate.c
 rpki_validator_SOURCES += object/crl.h object/crl.c
+rpki_validator_SOURCES += object/ghostbusters.h object/ghostbusters.c
 rpki_validator_SOURCES += object/manifest.h object/manifest.c
+rpki_validator_SOURCES += object/name.h object/name.c
 rpki_validator_SOURCES += object/roa.h object/roa.c
 rpki_validator_SOURCES += object/signed_object.h object/signed_object.c
 rpki_validator_SOURCES += object/tal.h object/tal.c
+rpki_validator_SOURCES += object/vcard.h object/vcard.c
 
 rpki_validator_SOURCES += resource/ip4.h resource/ip4.c
 rpki_validator_SOURCES += resource/ip6.h resource/ip6.c
index da9e99842438b37d1f76f2c02b6886b4416a3e77..8e98496899ad425c1ca8241c8f37df33385dcc20 100644 (file)
@@ -8,11 +8,16 @@
 
 /* These objects are expected to live on the stack. */
 struct oid_arcs {
+       char const *name;
        asn_oid_arc_t *arcs;
        size_t count;
 };
 
-#define OID2ARCS(oid) { .arcs = oid, .count = ARRAY_LEN(oid) }
+#define OID2ARCS(_name, oid) {                                         \
+       .name = _name,                                                  \
+       .arcs = oid,                                                    \
+       .count = ARRAY_LEN(oid),                                        \
+}
 
 void free_arcs(struct oid_arcs *);
 
@@ -32,6 +37,7 @@ typedef asn_oid_arc_t OID[];
 
 #define OID_ROA                      { 1, 2, 840, 113549, 1, 9, 16, 1, 24 }
 #define OID_MANIFEST                 { 1, 2, 840, 113549, 1, 9, 16, 1, 26 }
+#define OID_GHOSTBUSTERS             { 1, 2, 840, 113549, 1, 9, 16, 1, 35 }
 
 #define OID_RSA                      { 1, 2, 840, 113549, 1, 1, 1 }
 #define OID_SHA256                   { 2, 16, 840, 1, 101, 3, 4, 2, 1 }
index ab2be7eaf5031ee83b9c40ed42c4fc37ddeeed2c..80e094603045e837e8c667bdcd4247de014ca266 100644 (file)
@@ -1,9 +1,7 @@
 #include "common.h"
 
-#include <errno.h>
 #include <string.h>
 #include "log.h"
-#include "thread_var.h"
 
 /**
  * Does not assume that @string is NULL-terminated.
@@ -31,136 +29,3 @@ ia5s2string(ASN1_IA5STRING *ia5, char **result)
            ? pr_err("CRL URI IA5String has unused bits.")
            : string_clone(ia5->data, ia5->length, result);
 }
-
-/**
- * Only prints error message if the result is not 0 nor -ESRCH.
- */
-int
-x509_name_decode(X509_NAME *name, int nid, char **_result)
-{
-       char *result;
-       int len1, len2;
-
-       len1 = X509_NAME_get_text_by_NID(name, nid, NULL, 0);
-       if (len1 < 0)
-               return -ESRCH;
-
-       if (_result == NULL)
-               return 0;
-
-       result = calloc(len1 + 1, sizeof(char));
-       if (result == NULL)
-               return pr_enomem();
-
-       len2 = X509_NAME_get_text_by_NID(name, nid, result, len1 + 1);
-       if (len1 != len2) {
-               free(result);
-               return pr_err("Likely programming error: X509_NAME_get_text_by_NID() returned inconsistent lengths: %d,%d",
-                   len1, len2);
-       }
-
-       *_result = result;
-       return 0;
-}
-
-struct rfc5280_names {
-       char *commonName;
-       char *serialNumber;
-};
-
-static int
-get_names(X509_NAME *name, char const *what, struct rfc5280_names *result)
-{
-       int error;
-
-       error = x509_name_decode(name, NID_commonName, &result->commonName);
-       if (error == -ESRCH) {
-               return pr_err("The '%s' name lacks a commonName attribute.",
-                   what);
-       }
-       if (error)
-               return error;
-
-       error = x509_name_decode(name, NID_serialNumber, &result->serialNumber);
-       if (error == -ESRCH) {
-               result->serialNumber = NULL;
-               return 0;
-       }
-       if (error) {
-               free(result->commonName);
-               return error;
-       }
-
-       return 0;
-}
-
-/**
- * Also checks NULL.
- *
- * Does assume that @str1 and @str2 are NULL-terminated.
- */
-static bool str_equals(char const *str1, char const *str2)
-{
-       if (str1 == str2)
-               return true;
-       if (str1 == NULL || str2 == NULL)
-               return false;
-       return strcmp(str1, str2) == 0;
-}
-
-int
-validate_issuer_name(char const *container, X509_NAME *issuer)
-{
-       struct validation *state;
-       X509 *parent;
-       struct rfc5280_names parent_subject = { 0 };
-       struct rfc5280_names child_issuer = { 0 };
-       int error;
-
-       /*
-        * Not sure whether "the CRL issuer is the CA" means that the issuer
-        * name should equal the parent's subject name or not, because that's
-        * very much not what rfc6487#section-4.4 is asking us to check.
-        * But let's check it anyway.
-        */
-
-       state = state_retrieve();
-       if (state == NULL)
-               return -EINVAL;
-       parent = validation_peek_cert(state);
-       if (parent == NULL) {
-               return pr_err("%s appears to have no parent certificate.",
-                   container);
-       }
-
-       error = get_names(X509_get_subject_name(parent), "subject",
-           &parent_subject);
-       if (error)
-               return error;
-       error = get_names(issuer, "issuer", &child_issuer);
-       if (error)
-               goto end2;
-
-       if (strcmp(parent_subject.commonName, child_issuer.commonName) != 0) {
-               error = pr_err("%s's issuer commonName ('%s') does not equal issuer certificate's commonName ('%s').",
-                   container, parent_subject.commonName,
-                   child_issuer.commonName);
-               goto end1;
-       }
-
-       if (!str_equals(parent_subject.serialNumber,
-           child_issuer.serialNumber)) {
-               error = pr_err("%s's issuer serialNumber ('%s') does not equal issuer certificate's serialNumber ('%s').",
-                   container, parent_subject.serialNumber,
-                   child_issuer.serialNumber);
-               goto end1;
-       }
-
-end1:
-       free(child_issuer.commonName);
-       free(child_issuer.serialNumber);
-end2:
-       free(parent_subject.commonName);
-       free(parent_subject.serialNumber);
-       return error;
-}
index e109d437bd282052c3793cb66f9177f732d9d021..e78d09fd3897b63f97dab8d19026737c3597c616 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef SRC_RTR_COMMON_H_
 #define SRC_RTR_COMMON_H_
 
-#include <stdbool.h>
-#include <openssl/x509v3.h>
+#include <stddef.h>
+#include <openssl/x509.h>
 
 /* "I think that this is not supposed to be implemented." */
 #define ENOTSUPPORTED 3172
@@ -21,7 +21,4 @@
 int string_clone(void const *, size_t, char **);
 int ia5s2string(ASN1_IA5STRING *, char **);
 
-int x509_name_decode(X509_NAME *, int, char **);
-int validate_issuer_name(char const *, X509_NAME *);
-
 #endif /* SRC_RTR_COMMON_H_ */
index 4c2954c1b1e786b7a97cfd49629577c95fd2bb0a..54fb1e7f157827922440d98229778a1715eb0e91 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -66,7 +66,9 @@ pr_debug_prefix(void)
 void
 pr_debug_suffix(void)
 {
-       fprintf(STDOUT, "%s\n", COLOR_RESET);
+       if (config_get_color_output())
+               fprintf(STDOUT, "%s", COLOR_RESET);
+       fprintf(STDOUT, "\n");
 }
 
 void
index dd53abe8c4dbe44096e9a6a16980d6ff66df8806..be9209ede360b8a10b008d2f24595bbeaafe936b 100644 (file)
@@ -16,6 +16,7 @@
 #include "asn1/decode.h"
 #include "asn1/oid.h"
 #include "crypto/hash.h"
+#include "object/name.h"
 #include "rsync/rsync.h"
 
 /* Just to prevent some line breaking. */
index 059eaa141d2a3b62b1dcd864cc5cd9ce949899ea..10808ad93f14221d5ca7acd5b8fe6606936f1142 100644 (file)
@@ -2,10 +2,10 @@
 
 #include <errno.h>
 #include "algorithm.h"
-#include "common.h"
 #include "extension.h"
 #include "log.h"
 #include "thread_var.h"
+#include "object/name.h"
 
 static int
 __crl_load(struct rpki_uri const *uri, X509_CRL **result)
diff --git a/src/object/ghostbusters.c b/src/object/ghostbusters.c
new file mode 100644 (file)
index 0000000..83fb7ae
--- /dev/null
@@ -0,0 +1,44 @@
+#include "object/ghostbusters.h"
+
+#include "log.h"
+#include "thread_var.h"
+#include "asn1/oid.h"
+#include "asn1/signed_data.h"
+#include "object/signed_object.h"
+#include "vcard.h"
+
+static int
+handle_vcard(OCTET_STRING_t *vcard, void *arg)
+{
+       return handle_ghostbusters_vcard(vcard);
+}
+
+int
+handle_ghostbusters(struct rpki_uri const *uri, struct rpp *pp,
+    STACK_OF(X509_CRL) *crls)
+{
+       static OID oid = OID_GHOSTBUSTERS;
+       struct oid_arcs arcs = OID2ARCS("ghostbusters", oid);
+       struct signed_object_args sobj_args;
+       int error;
+
+       pr_debug_add("Ghostbusters %s {", uri->global);
+       fnstack_push(uri->global);
+
+       error = signed_object_args_init(&sobj_args, uri, crls);
+       if (error)
+               goto end1;
+
+       error = signed_object_decode(&sobj_args, &arcs, handle_vcard, NULL);
+       if (error)
+               goto end2;
+
+       error = refs_validate_ee(&sobj_args.refs, pp, sobj_args.uri);
+
+end2:
+       signed_object_args_cleanup(&sobj_args);
+end1:
+       pr_debug_rm("}");
+       fnstack_pop();
+       return error;
+}
diff --git a/src/object/ghostbusters.h b/src/object/ghostbusters.h
new file mode 100644 (file)
index 0000000..d5610cb
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef SRC_OBJECT_GHOSTBUSTERS_H_
+#define SRC_OBJECT_GHOSTBUSTERS_H_
+
+#include <openssl/x509.h>
+#include "uri.h"
+#include "rpp.h"
+
+int handle_ghostbusters(struct rpki_uri const *, struct rpp *,
+    STACK_OF(X509_CRL) *);
+
+#endif /* SRC_OBJECT_GHOSTBUSTERS_H_ */
index 94cacb86a2fc915dea27fbaddda6198ff4c6d6b0..534a32cad64ab891baed8e9940e6830752fe77bc 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "log.h"
 #include "thread_var.h"
+#include "asn1/decode.h"
 #include "asn1/oid.h"
 #include "crypto/hash.h"
 #include "object/certificate.h"
@@ -18,6 +19,12 @@ struct manifest {
        char const *file_path;
 };
 
+static int
+manifest_decode(OCTET_STRING_t *string, void *arg)
+{
+       return asn1_decode_octet_string(string, &asn_DEF_Manifest, arg);
+}
+
 static int
 validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next)
 {
@@ -146,6 +153,8 @@ __handle_manifest(struct manifest *mft, struct rpp **pp)
                        error = rpp_add_roa(*pp, &uri);
                else if (uri_has_extension(&uri, ".crl"))
                        error = rpp_add_crl(*pp, &uri);
+               else if (uri_has_extension(&uri, ".gbr"))
+                       error = rpp_add_ghostbusters(*pp, &uri);
                else
                        uri_cleanup(&uri); /* ignore it. */
 
@@ -171,7 +180,7 @@ handle_manifest(struct rpki_uri const *uri, STACK_OF(X509_CRL) *crls,
     struct rpp **pp)
 {
        static OID oid = OID_MANIFEST;
-       struct oid_arcs arcs = OID2ARCS(oid);
+       struct oid_arcs arcs = OID2ARCS("manifest", oid);
        struct signed_object_args sobj_args;
        struct manifest mft;
        int error;
@@ -184,8 +193,8 @@ handle_manifest(struct rpki_uri const *uri, STACK_OF(X509_CRL) *crls,
                goto end1;
        mft.file_path = uri->global;
 
-       error = signed_object_decode(&sobj_args, &asn_DEF_Manifest, &arcs,
-           (void **) &mft.obj);
+       error = signed_object_decode(&sobj_args, &arcs,
+           manifest_decode, &mft.obj);
        if (error)
                goto end2;
 
diff --git a/src/object/name.c b/src/object/name.c
new file mode 100644 (file)
index 0000000..2dae689
--- /dev/null
@@ -0,0 +1,141 @@
+#include "object/name.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "log.h"
+#include "thread_var.h"
+
+/**
+ * Only prints error message if the result is not 0 nor -ESRCH.
+ */
+int
+x509_name_decode(X509_NAME *name, int nid, char **_result)
+{
+       char *result;
+       int len1, len2;
+
+       len1 = X509_NAME_get_text_by_NID(name, nid, NULL, 0);
+       if (len1 < 0)
+               return -ESRCH;
+
+       if (_result == NULL)
+               return 0;
+
+       result = calloc(len1 + 1, sizeof(char));
+       if (result == NULL)
+               return pr_enomem();
+
+       len2 = X509_NAME_get_text_by_NID(name, nid, result, len1 + 1);
+       if (len1 != len2) {
+               free(result);
+               return pr_err("Likely programming error: X509_NAME_get_text_by_NID() returned inconsistent lengths: %d,%d",
+                   len1, len2);
+       }
+
+       *_result = result;
+       return 0;
+}
+
+struct rfc5280_names {
+       char *commonName;
+       char *serialNumber;
+};
+
+static int
+get_names(X509_NAME *name, char const *what, struct rfc5280_names *result)
+{
+       int error;
+
+       error = x509_name_decode(name, NID_commonName, &result->commonName);
+       if (error == -ESRCH) {
+               return pr_err("The '%s' name lacks a commonName attribute.",
+                   what);
+       }
+       if (error)
+               return error;
+
+       error = x509_name_decode(name, NID_serialNumber, &result->serialNumber);
+       if (error == -ESRCH) {
+               result->serialNumber = NULL;
+               return 0;
+       }
+       if (error) {
+               free(result->commonName);
+               return error;
+       }
+
+       return 0;
+}
+
+/**
+ * Also checks NULL.
+ *
+ * Does assume that @str1 and @str2 are NULL-terminated.
+ */
+static bool str_equals(char const *str1, char const *str2)
+{
+       if (str1 == str2)
+               return true;
+       if (str1 == NULL || str2 == NULL)
+               return false;
+       return strcmp(str1, str2) == 0;
+}
+
+int
+validate_issuer_name(char const *container, X509_NAME *issuer)
+{
+       struct validation *state;
+       X509 *parent;
+       struct rfc5280_names parent_subject = { 0 };
+       struct rfc5280_names child_issuer = { 0 };
+       int error;
+
+       /*
+        * Not sure whether "the CRL issuer is the CA" means that the issuer
+        * name should equal the parent's subject name or not, because that's
+        * very much not what rfc6487#section-4.4 is asking us to check.
+        * But let's check it anyway.
+        */
+
+       state = state_retrieve();
+       if (state == NULL)
+               return -EINVAL;
+       parent = validation_peek_cert(state);
+       if (parent == NULL) {
+               return pr_err("%s appears to have no parent certificate.",
+                   container);
+       }
+
+       error = get_names(X509_get_subject_name(parent), "subject",
+           &parent_subject);
+       if (error)
+               return error;
+       error = get_names(issuer, "issuer", &child_issuer);
+       if (error)
+               goto end2;
+
+       if (strcmp(parent_subject.commonName, child_issuer.commonName) != 0) {
+               error = pr_err("%s's issuer commonName ('%s') does not equal issuer certificate's commonName ('%s').",
+                   container, parent_subject.commonName,
+                   child_issuer.commonName);
+               goto end1;
+       }
+
+       if (!str_equals(parent_subject.serialNumber,
+           child_issuer.serialNumber)) {
+               error = pr_err("%s's issuer serialNumber ('%s') does not equal issuer certificate's serialNumber ('%s').",
+                   container, parent_subject.serialNumber,
+                   child_issuer.serialNumber);
+               goto end1;
+       }
+
+end1:
+       free(child_issuer.commonName);
+       free(child_issuer.serialNumber);
+end2:
+       free(parent_subject.commonName);
+       free(parent_subject.serialNumber);
+       return error;
+}
diff --git a/src/object/name.h b/src/object/name.h
new file mode 100644 (file)
index 0000000..fb0fcf8
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef SRC_OBJECT_NAME_H_
+#define SRC_OBJECT_NAME_H_
+
+#include <openssl/x509.h>
+
+int x509_name_decode(X509_NAME *, int, char **);
+int validate_issuer_name(char const *, X509_NAME *);
+
+#endif /* SRC_OBJECT_NAME_H_ */
index 9948f5a209e237e7698b2e4233a4a689044fdb5e..c4339880e442ccd4b488974ff7e3685e6fae8538 100644 (file)
@@ -6,9 +6,17 @@
 
 #include "log.h"
 #include "thread_var.h"
+#include "asn1/decode.h"
 #include "asn1/oid.h"
 #include "object/signed_object.h"
 
+static int
+roa_decode(OCTET_STRING_t *string, void *arg)
+{
+       return asn1_decode_octet_string(string, &asn_DEF_RouteOriginAttestation,
+           arg);
+}
+
 static int
 print_addr4(struct resources *parent, unsigned long asn,
     struct ROAIPAddress *roa_addr)
@@ -196,7 +204,7 @@ handle_roa(struct rpki_uri const *uri, struct rpp *pp,
     STACK_OF(X509_CRL) *crls)
 {
        static OID oid = OID_ROA;
-       struct oid_arcs arcs = OID2ARCS(oid);
+       struct oid_arcs arcs = OID2ARCS("roa", oid);
        struct signed_object_args sobj_args;
        struct RouteOriginAttestation *roa;
        int error;
@@ -208,8 +216,7 @@ handle_roa(struct rpki_uri const *uri, struct rpp *pp,
        if (error)
                goto end1;
 
-       error = signed_object_decode(&sobj_args,
-           &asn_DEF_RouteOriginAttestation, &arcs, (void **) &roa);
+       error = signed_object_decode(&sobj_args, &arcs, roa_decode, &roa);
        if (error)
                goto end2;
 
index 644c6be132ec1b72b9e92af4300097908d78463b..e7949d7aae422339dda37d0ee5e486a69bf2686e 100644 (file)
@@ -3,12 +3,9 @@
 #include <errno.h>
 #include "log.h"
 #include "asn1/content_info.h"
-#include "asn1/decode.h"
 
 static int
-validate_eContentType(struct SignedData *sdata,
-    asn_TYPE_descriptor_t const *descriptor,
-    struct oid_arcs const *oid)
+validate_eContentType(struct SignedData *sdata, struct oid_arcs const *oid)
 {
        struct oid_arcs arcs;
        bool equals;
@@ -20,17 +17,15 @@ validate_eContentType(struct SignedData *sdata,
        equals = arcs_equal(&arcs, oid);
        free_arcs(&arcs);
        if (!equals) {
-               return pr_err("SignedObject's encapContentInfo lacks the OID of a %s.",
-                   descriptor->name);
+               return pr_err("The OID of the SignedObject's encapContentInfo is not '%s'.",
+                   oid->name);
        }
 
        return 0;
 }
 
 static int
-validate_content_type(struct SignedData *sdata,
-    asn_TYPE_descriptor_t const *descriptor,
-    struct oid_arcs const *oid)
+validate_content_type(struct SignedData *sdata, struct oid_arcs const *oid)
 {
        OBJECT_IDENTIFIER_t *ctype;
        struct oid_arcs arcs;
@@ -47,8 +42,8 @@ validate_content_type(struct SignedData *sdata,
        equals = arcs_equal(&arcs, oid);
        free_arcs(&arcs);
        if (!equals) {
-               return pr_err("SignedObject's content type attribute lacks the OID of a %s.",
-                   descriptor->name);
+               return pr_err("The OID of the SignedObject's content type attribute is not '%s'.",
+                   oid->name);
        }
 
        return 0;
@@ -56,9 +51,9 @@ validate_content_type(struct SignedData *sdata,
 
 int
 signed_object_decode(struct signed_object_args *args,
-    asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid,
-    void **result)
+    signed_object_cb cb,
+    void *cb_arg)
 {
        struct ContentInfo *cinfo;
        struct SignedData *sdata;
@@ -75,18 +70,17 @@ signed_object_decode(struct signed_object_args *args,
        /* rfc6482#section-2 */
        /* rfc6486#section-4.1 */
        /* rfc6486#section-4.4.1 */
-       error = validate_eContentType(sdata, descriptor, oid);
+       error = validate_eContentType(sdata, oid);
        if (error)
                goto end3;
 
        /* rfc6482#section-2 */
        /* rfc6486#section-4.3 */
-       error = validate_content_type(sdata, descriptor, oid);
+       error = validate_content_type(sdata, oid);
        if (error)
                goto end3;
 
-       error = asn1_decode_octet_string(sdata->encapContentInfo.eContent,
-           descriptor, result);
+       error = cb(sdata->encapContentInfo.eContent, cb_arg);
 
 end3:  signed_data_free(sdata);
 end2:  content_info_free(cinfo);
index 6898a6373e2af3f181629df0d9fac1fd079a1777..41dc55867396765dfd27d536b5d228edbfdc5109 100644 (file)
@@ -5,7 +5,9 @@
 #include "asn1/oid.h"
 #include "asn1/signed_data.h"
 
-int signed_object_decode(struct signed_object_args *args,
-    asn_TYPE_descriptor_t const *, struct oid_arcs const *, void **);
+typedef int (*signed_object_cb)(OCTET_STRING_t *, void *);
+
+int signed_object_decode(struct signed_object_args *, struct oid_arcs const *,
+    signed_object_cb, void *);
 
 #endif /* SRC_OBJECT_SIGNED_OBJECT_H_ */
diff --git a/src/object/vcard.c b/src/object/vcard.c
new file mode 100644 (file)
index 0000000..e20a446
--- /dev/null
@@ -0,0 +1,277 @@
+#include "vcard.h"
+
+#include <errno.h>
+#include <stdbool.h>
+
+#include "log.h"
+
+/**
+ * Reminder: UTF-8 strings are **not** C strings.
+ * They can contain null characters, which do not terminate them.
+ * DO NOT use standard string operations on them. (Except for the `n` ones).
+ */
+struct utf8_string {
+       uint8_t *val;
+       /** Number of bytes in @val. */
+       size_t len;
+       /** Actual allocated size of @val. */
+       size_t size;
+};
+
+struct vcard_line {
+       /**
+        * This is a copy of the actual vCard value.
+        * It does not include the newline and has folding characters removed.
+        */
+       struct utf8_string str;
+       size_t octet_string_offset;
+};
+
+enum string_analysis {
+       SA_COPY_CHARA,
+       SA_LINE_ENDED,
+       SA_SKIP_THREE_CHARAS,
+       SA_ERROR,
+};
+
+/**
+ * Returns a pointer to the character at the right of string[pos], if it exists.
+ *
+ * Assumes that string->len > 0.
+ */
+static uint8_t *
+next_chara(struct utf8_string *string, size_t pos)
+{
+       return (pos < string->len - 1) ? (string->val + pos + 1) : NULL;
+}
+
+/**
+ * Assumes that pos < string_len and that string_len > 0.
+ */
+static enum string_analysis
+analyze_pos(struct utf8_string *string, size_t pos)
+{
+       /*
+        * Special cases:
+        *
+        * 1. \r\n: Normal line/property end
+        * 2. \r\n\w (where '\w' is space or tab): Folded line
+        *
+        * Each vCard line must end with \r\n.
+        */
+
+       uint8_t *next1;
+       uint8_t *next2;
+
+       if (string->val[pos] != '\r')
+               return SA_COPY_CHARA; /* Typical path */
+
+       /* At this point, we have a \r. */
+
+       next1 = next_chara(string, pos);
+       if (next1 == NULL) {
+               pr_err("vCard's final newline is incomplete ('\\r').");
+               return SA_ERROR;
+       }
+       if (*next1 != '\n')
+               return SA_COPY_CHARA; /* Random stray \r; no problem for now. */
+
+       /* At this point, we have a \r\n. */
+
+       next2 = next_chara(string, pos + 1);
+       if (next2 == NULL)
+               return SA_LINE_ENDED; /* \r\n<eof> */
+       if (*next2 == ' ' || *next2 == '\t')
+               return SA_SKIP_THREE_CHARAS; /* Folded line */
+
+       return SA_LINE_ENDED; /* \r\n<more lines> */
+}
+
+static int
+double_line_size(struct vcard_line *line)
+{
+       uint8_t *tmp;
+
+       line->str.size *= 2;
+       tmp = realloc(line->str.val, line->str.size);
+       if (tmp == NULL)
+               return pr_enomem();
+       line->str.val = tmp;
+
+       return 0;
+}
+
+static int
+add_chara(struct vcard_line *line, uint8_t chara, bool inc_str_len)
+{
+       int error;
+
+       if (line->str.len + 1 == line->str.size) {
+               error = double_line_size(line);
+               if (error)
+                       return error;
+       }
+
+       line->str.val[line->str.len] = chara;
+       if (inc_str_len)
+               line->str.len++;
+
+       return 0;
+}
+
+/**
+ * Will remove the newline (\r\n). Just assume that there is a valid newline at
+ * the end.
+ * The result will not be a C string, but will still have a null character at
+ * the end (not accounted by line->str.len), in case you want to print it.
+ */
+static int
+line_next(struct vcard_line *line, OCTET_STRING_t *string8)
+{
+       struct utf8_string string;
+       size_t string_pos;
+       int error;
+
+       if (string8->size == line->octet_string_offset)
+               return pr_err("vCard ends prematurely. (Expected an END line)");
+
+       string.val = string8->buf + line->octet_string_offset;
+       string.len = string8->size - line->octet_string_offset;
+       string.size = 0;
+       line->str.len = 0;
+
+       for (string_pos = 0; string_pos < string.len; string_pos++) {
+               switch (analyze_pos(&string, string_pos)) {
+               case SA_COPY_CHARA:
+                       error = add_chara(line, string.val[string_pos], true);
+                       if (error)
+                               return error;
+                       break;
+
+               case SA_LINE_ENDED:
+                       line->octet_string_offset += string_pos + 2;
+                       return add_chara(line, 0, false);
+
+               case SA_SKIP_THREE_CHARAS:
+                       string_pos += 2;
+                       break;
+
+               case SA_ERROR:
+                       return -EINVAL;
+               }
+       }
+
+       return pr_err("vCard line does not end with a \\r\\n-style newline.");
+}
+
+static int
+line_validate(struct vcard_line *line, char const *expected)
+{
+       size_t expected_len = strlen(expected);
+
+       if (line->str.len != expected_len)
+               goto fail;
+
+       /*
+        * RFC 6350:
+        * "Property names and parameter names are case-insensitive"
+        * "Parameter values that are not explicitly defined as being
+        * case-sensitive are case-insensitive."
+        */
+       if (strncasecmp((char *) line->str.val, expected, expected_len) != 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       return pr_err("Expected vCard property '%s', got '%s'.",
+           expected, line->str.val);
+}
+
+/**
+ * @tag must contain the colon. This simplifies the code.
+ */
+static int
+line_starts_with(struct vcard_line *line, char const *tag)
+{
+       /* RFC6350: "Property names and parameter names are case-insensitive" */
+       return strncasecmp((char *) line->str.val, tag, strlen(tag)) == 0;
+}
+
+static int
+__handle_ghostbusters_vcard(OCTET_STRING_t *vcard, struct vcard_line *line)
+{
+       bool fn_found = false;
+       bool useful_found = false;
+       int error;
+
+       error = line_next(line, vcard);
+       if (error)
+               return error;
+       error = line_validate(line, "BEGIN:VCARD");
+       if (error)
+               return error;
+
+       error = line_next(line, vcard);
+       if (error)
+               return error;
+       error = line_validate(line, "VERSION:4.0");
+       if (error)
+               return error;
+
+       do {
+               error = line_next(line, vcard);
+               if (error)
+                       return error;
+
+               if (line_starts_with(line, "FN:")) {
+                       fn_found = true;
+
+               } else if (line_starts_with(line, "ORG:")
+                   || line_starts_with(line, "ADR:")
+                   || line_starts_with(line, "TEL:")
+                   || line_starts_with(line, "EMAIL:")) {
+                       useful_found = true;
+
+               } else if (line_starts_with(line, "END:")) {
+                       break;
+
+               } else {
+                       return pr_err("Unexpected vCard line: '%s'",
+                           line->str.val);
+               }
+
+       } while (true);
+
+       error = line_validate(line, "END:VCARD");
+       if (error)
+               return error;
+       if (vcard->size != line->octet_string_offset)
+               return pr_err("vCard has content after the END tag.");
+
+       if (!fn_found)
+               return pr_err("vCard lacks the 'FN' property.");
+       if (!useful_found)
+               return pr_err("vCard lacks the 'ORG', 'ADR', 'TEL' and/or 'EMAIL' properties.");
+
+       return 0;
+}
+
+int
+handle_ghostbusters_vcard(OCTET_STRING_t *vcard)
+{
+       struct vcard_line line;
+       int error;
+
+       line.str.size = 81; /* Okay default, assuming there is no folding. */
+       line.str.len = 0;
+       line.str.val = malloc(line.str.size);
+       if (line.str.val == NULL)
+               return pr_enomem();
+       line.octet_string_offset = 0;
+
+       error = __handle_ghostbusters_vcard(vcard, &line);
+
+       free(line.str.val);
+       return error;
+}
diff --git a/src/object/vcard.h b/src/object/vcard.h
new file mode 100644 (file)
index 0000000..b25fc0c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef SRC_OBJECT_VCARD_H_
+#define SRC_OBJECT_VCARD_H_
+
+#include <libcmscodec/OCTET_STRING.h>
+
+int handle_ghostbusters_vcard(OCTET_STRING_t *);
+
+#endif /* SRC_OBJECT_VCARD_H_ */
index ca769c27a855ffdfcfb6a54b5af772f84db3dec7..b7992f831f5f2c3648053205abdcfbde0e264c6f 100644 (file)
--- a/src/rpp.c
+++ b/src/rpp.c
@@ -7,6 +7,7 @@
 #include "uri.h"
 #include "object/certificate.h"
 #include "object/crl.h"
+#include "object/ghostbusters.h"
 #include "object/roa.h"
 
 ARRAY_LIST(uris, struct rpki_uri)
@@ -21,6 +22,8 @@ struct rpp {
        /* The Manifest is not needed for now. */
 
        struct uris roas; /* Route Origin Attestations */
+
+       struct uris ghostbusters;
 };
 
 struct rpp *
@@ -37,9 +40,13 @@ rpp_create(void)
        result->crl_set = false;
        if (uris_init(&result->roas) != 0)
                goto fail3;
+       if (uris_init(&result->ghostbusters) != 0)
+               goto fail4;
 
        return result;
 
+fail4:
+       uris_cleanup(&result->roas, uri_cleanup);
 fail3:
        uris_cleanup(&result->certs, uri_cleanup);
 fail2:
@@ -53,6 +60,7 @@ rpp_destroy(struct rpp *pp)
 {
        uris_cleanup(&pp->certs, uri_cleanup);
        uris_cleanup(&pp->roas, uri_cleanup);
+       uris_cleanup(&pp->ghostbusters, uri_cleanup);
        free(pp);
 }
 
@@ -68,6 +76,12 @@ rpp_add_roa(struct rpp *pp, struct rpki_uri *uri)
        return uris_add(&pp->roas, uri);
 }
 
+int
+rpp_add_ghostbusters(struct rpp *pp, struct rpki_uri *uri)
+{
+       return uris_add(&pp->ghostbusters, uri);
+}
+
 int
 rpp_add_crl(struct rpp *pp, struct rpki_uri *uri)
 {
@@ -132,14 +146,16 @@ rpp_traverse(struct rpp *pp)
                goto end;
 
        /* Use CRL stack to validate certificates, and also traverse them. */
-       ARRAYLIST_FOREACH(&pp->certs, uri) {
+       ARRAYLIST_FOREACH(&pp->certs, uri)
                certificate_traverse(pp, uri, crls, false);
-       }
 
        /* Use valid address ranges to print ROAs that match them. */
        ARRAYLIST_FOREACH(&pp->roas, uri)
                handle_roa(uri, pp, crls);
 
+       ARRAYLIST_FOREACH(&pp->ghostbusters, uri)
+               handle_ghostbusters(uri, pp, crls);
+
 end:
        sk_X509_CRL_pop_free(crls, X509_CRL_free);
        return error;
index 6b687f5a9d114280b6331d6c91697333d78e25be..7d17dbd12dbba139d9a2da1110663cc1f832cfdb 100644 (file)
--- a/src/rpp.h
+++ b/src/rpp.h
@@ -11,6 +11,7 @@ void rpp_destroy(struct rpp *);
 int rpp_add_cert(struct rpp *, struct rpki_uri *);
 int rpp_add_crl(struct rpp *, struct rpki_uri *);
 int rpp_add_roa(struct rpp *, struct rpki_uri *);
+int rpp_add_ghostbusters(struct rpp *, struct rpki_uri *);
 
 struct rpki_uri const *rpp_get_crl(struct rpp const *);
 
index 552ffc09bd3a98c2a76d28e93d8cd1d6182dd6ec..30433b63269e0c42cf6c13b6a487302f2a6c6f9b 100644 (file)
@@ -25,8 +25,6 @@ static struct uri_list *rsync_uris;
 static char const *const RSYNC_PREFIX = "rsync://";
 static bool rsync_enabled;
 
-//static const char *rsync_command[] = {"rsync", "--recursive", "--delete", "--times", NULL};
-
 int
 rsync_init(bool is_rsync_enable)
 {
index 7ee418377d4dc9c584393ace4940566f2530df04..07ba2032de336dc98f7400411551efd48f92f182 100644 (file)
@@ -373,10 +373,18 @@ validation_store_subject(struct validation *state, char *subject)
        if (cert == NULL)
                return 0; /* The TA lacks siblings, so subject is unique. */
 
-       ARRAYLIST_FOREACH(&cert->subjects, cursor)
-               if (strcmp(*cursor, subject) == 0)
-                       return pr_err("Subject name '%s' is not unique.",
+       ARRAYLIST_FOREACH(&cert->subjects, cursor) {
+               if (strcmp(*cursor, subject) == 0) {
+                       /*
+                        * I had to downgrade this because lots of people are
+                        * breaking this rule.
+                        * TODO (next iteration) make a framework so the user
+                        * can choose the severity of each error.
+                        */
+                       return pr_warn("Subject name '%s' is not unique.",
                            subject);
+               }
+       }
 
        duplicate = strdup(subject);
        if (duplicate == NULL)
index b4c72bff297554f06ef2c73227c16551334834d1..5bb2c4a546c06303291b3394e6d10a1d181933f5 100644 (file)
@@ -8,35 +8,45 @@
 
 AM_CFLAGS = -pedantic -Wall -std=gnu11 -I../src -DUNIT_TESTING ${CHECK_CFLAGS}
 MY_LDADD = ${CHECK_LIBS}
-
-check_PROGRAMS = address.test line_file.test rsync.test tal.test
+BASIC_MODULES  = ../src/config.c ../src/config.h
+BASIC_MODULES += ../src/log.c ../src/log.h
+BASIC_MODULES += impersonator.c
+
+check_PROGRAMS  = address.test
+check_PROGRAMS += vcard.test
+check_PROGRAMS += line_file.test
+check_PROGRAMS += rsync.test
+check_PROGRAMS += tal.test
 TESTS = ${check_PROGRAMS}
 
-address_test_SOURCES = ../src/address.h
-address_test_SOURCES += ../src/log.c ../src/log.h
+address_test_SOURCES  = ${BASIC_MODULES}
+address_test_SOURCES += ../src/address.h
 address_test_SOURCES += address_test.c
 address_test_LDADD = ${MY_LDADD}
 
-line_file_test_SOURCES = ../src/file.c ../src/file.h ../src/log.c ../src/log.h
-line_file_test_SOURCES += ../src/common.c ../src/common.h
-line_file_test_SOURCES += line_file_test.c ../src/line_file.c ../src/line_file.h
+vcard_test_SOURCES  = ${BASIC_MODULES}
+vcard_test_SOURCES += vcard_test.c
+vcard_test_LDADD = ${MY_LDADD}
+
+line_file_test_SOURCES  = ${BASIC_MODULES}
+line_file_test_SOURCES += ../src/file.c ../src/file.h
+line_file_test_SOURCES += ../src/line_file.c ../src/line_file.h
+line_file_test_SOURCES += line_file_test.c
 line_file_test_LDADD = ${MY_LDADD}
 
-tal_test_SOURCES = ../src/file.c ../src/file.h ../src/log.c ../src/log.h
-tal_test_SOURCES += ../src/config.c ../src/config.h
-tal_test_SOURCES += ../src/toml_handler.c ../src/toml_handler.h
+rsync_test_SOURCES  = ${BASIC_MODULES}
+rsync_test_SOURCES += ../src/common.c ../src/common.h
+rsync_test_SOURCES += ../src/uri.c ../src/uri.h
+rsync_test_SOURCES += rsync_test.c
+rsync_test_LDADD = ${MY_LDADD}
+
+tal_test_SOURCES  = ${BASIC_MODULES}
+tal_test_SOURCES += ../src/file.c ../src/file.h
 tal_test_SOURCES += ../src/common.c ../src/common.h
 tal_test_SOURCES += ../src/crypto/base64.c ../src/crypto/base64.h
 tal_test_SOURCES += ../src/random.c ../src/random.h
 tal_test_SOURCES += ../src/uri.c ../src/uri.h
-tal_test_SOURCES += tal_test.c ../src/line_file.c ../src/line_file.h
-tal_test_CFLAGS = ${AM_CFLAGS} ${GLIB_CFLAGS}
-tal_test_LDADD = ${MY_LDADD} ${GLIB_LIBS}
-
-rsync_test_SOURCES = ../src/file.c ../src/file.h ../src/log.c ../src/log.h
-rsync_test_SOURCES += ../src/config.c ../src/config.h
-rsync_test_SOURCES += ../src/toml_handler.c ../src/toml_handler.h
-rsync_test_SOURCES += ../src/uri.c ../src/uri.h
-rsync_test_SOURCES += ../src/common.c ../src/common.h
-rsync_test_SOURCES += rsync_test.c
-rsync_test_LDADD = ${MY_LDADD}
+tal_test_SOURCES += ../src/line_file.c ../src/line_file.h
+tal_test_SOURCES += tal_test.c
+tal_test_CFLAGS = ${AM_CFLAGS}
+tal_test_LDADD = ${MY_LDADD}
diff --git a/test/impersonator.c b/test/impersonator.c
new file mode 100644 (file)
index 0000000..3ae99a6
--- /dev/null
@@ -0,0 +1,38 @@
+#include <arpa/inet.h>
+
+/**
+ * Some core functions, as linked from unit testing code.
+ */
+
+static char addr_buffer1[INET6_ADDRSTRLEN];
+static char addr_buffer2[INET6_ADDRSTRLEN];
+
+char const *
+v4addr2str(struct in_addr *addr)
+{
+       return inet_ntop(AF_INET, addr, addr_buffer1, sizeof(addr_buffer1));
+}
+
+char const *
+v4addr2str2(struct in_addr *addr)
+{
+       return inet_ntop(AF_INET, addr, addr_buffer2, sizeof(addr_buffer2));
+}
+
+char const *
+v6addr2str(struct in6_addr *addr)
+{
+       return inet_ntop(AF_INET6, addr, addr_buffer1, sizeof(addr_buffer1));
+}
+
+char const *
+v6addr2str2(struct in6_addr *addr)
+{
+       return inet_ntop(AF_INET6, addr, addr_buffer2, sizeof(addr_buffer2));
+}
+
+int
+set_config_from_file(char *config_file)
+{
+       return 0;
+}
index ed8004a76a502f906fbe58c168d85266319f5936..51290de2962269cb91d9c59f63a4f0f816826ea8 100644 (file)
@@ -83,7 +83,7 @@ START_TEST(file_line_null_chara)
 }
 END_TEST
 
-Suite *lfile_read_suite(void)
+Suite *ghostbusters_suite(void)
 {
        Suite *suite;
        TCase *core, *limits, *errors;
@@ -110,7 +110,7 @@ int main(void)
        SRunner *runner;
        int tests_failed;
 
-       suite = lfile_read_suite();
+       suite = ghostbusters_suite();
 
        runner = srunner_create(suite);
        srunner_run_all(runner, CK_NORMAL);
index 77d6373f9047395a2211276ba876b3a71058b012..b0f455edbddfc10775962b090a0591129fd45119 100644 (file)
@@ -105,7 +105,7 @@ test_get_path(char *test, char *expected)
 
        error = get_path_only(string, strlen(string), rsync_prefix_len, &result);
        if (error) {
-//             free(string);
+               free(string);
                return;
        }
 
diff --git a/test/vcard_test.c b/test/vcard_test.c
new file mode 100644 (file)
index 0000000..6747d66
--- /dev/null
@@ -0,0 +1,127 @@
+#include "object/vcard.c"
+
+#include <check.h>
+
+#define VC_BEGIN       "BEGIN:VCARD\r\n"
+#define VC_VERSION     "VERSION:4.0\r\n"
+#define VC_FN          "FN:name\r\n"
+#define VC_ORG         "ORG:organization\r\n"
+#define VC_ADR         "ADR:address\r\n"
+#define VC_TEL         "TEL:12345678\r\n"
+#define VC_EMAIL       "EMAIL:e@ma.il\r\n"
+#define VC_END         "END:VCARD\r\n"
+
+#define INIT_STR8(name, str) do {                                      \
+       printf("- %s:\n", name);                                        \
+       str8.buf = (uint8_t *) (str);                                   \
+       str8.size = strlen(str);                                        \
+} while (0)
+
+START_TEST(vcard_normal)
+{
+       OCTET_STRING_t str8;
+
+       INIT_STR8(
+           "Minimal",
+           VC_BEGIN VC_VERSION VC_FN VC_ORG VC_END
+       );
+       ck_assert_int_eq(0, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Full",
+           VC_BEGIN VC_VERSION VC_FN VC_ORG VC_ADR VC_TEL VC_EMAIL VC_END
+       );
+       ck_assert_int_eq(0, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Missing locator",
+           VC_BEGIN VC_VERSION VC_FN VC_END
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Missing name",
+           VC_BEGIN VC_VERSION VC_ORG VC_END
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Unknown property",
+           VC_BEGIN VC_VERSION VC_FN VC_ORG "POTATO:potato\r\n" VC_END
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "No newline",
+           VC_BEGIN VC_VERSION "FN:name" VC_ORG VC_END
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "\\r newline",
+           VC_BEGIN VC_VERSION "FN:name\r" VC_ORG VC_END
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "\\n newline",
+           VC_BEGIN VC_VERSION "FN:name\n" VC_ORG VC_END
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Last line has no valid newline",
+           VC_BEGIN VC_VERSION VC_FN VC_ORG "END:VCARD"
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Stray null character (in non-constant)",
+           VC_BEGIN VC_VERSION "FN:n\0ame\r\n" VC_ORG VC_END
+       );
+       str8.size += strlen(" ame\r\n" VC_ORG VC_END);
+       ck_assert_int_eq(0, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Stray null character (in constant)",
+           VC_BEGIN "VERSION:4.\00\r\n" VC_FN VC_ORG VC_END
+       );
+       str8.size += strlen(" 0\r\n" VC_FN VC_ORG VC_END);
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+
+       INIT_STR8(
+           "Garbage after END",
+           VC_BEGIN VC_VERSION VC_FN VC_ORG VC_END VC_EMAIL
+       );
+       ck_assert_int_eq(-EINVAL, handle_ghostbusters_vcard(&str8));
+}
+END_TEST
+
+Suite *ghostbusters_suite(void)
+{
+       Suite *suite;
+       TCase *hgv;
+
+       hgv = tcase_create("handle_ghostbusters_vcard()");
+       tcase_add_test(hgv, vcard_normal);
+
+       suite = suite_create("vCard");
+       suite_add_tcase(suite, hgv);
+       return suite;
+}
+
+int main(void)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = ghostbusters_suite();
+
+       runner = srunner_create(suite);
+       srunner_run_all(runner, CK_NORMAL);
+       tests_failed = srunner_ntests_failed(runner);
+       srunner_free(runner);
+
+       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}