]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Add actual certificate tree validation and other misc tweaks
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Thu, 15 Nov 2018 20:11:47 +0000 (14:11 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Thu, 15 Nov 2018 20:11:47 +0000 (14:11 -0600)
The tweaks are

1. Unified error message printing. Probably not the final version.
2. Add validation state object, meant to be passed around everywhere.
   Prevents global variables.
3. Add a sketch of the CRL code. WIP.

29 files changed:
src/Makefile.am
src/asn1/content_info.c
src/asn1/content_info.h
src/asn1/decode.c
src/asn1/decode.h
src/asn1/oid.c
src/asn1/oid.h
src/asn1/signed_data.c
src/asn1/signed_data.h
src/common.c
src/common.h
src/file.c
src/file.h
src/line_file.c
src/log.c [new file with mode: 0644]
src/log.h [new file with mode: 0644]
src/main.c [moved from src/main2.c with 67% similarity]
src/object/certificate.c
src/object/certificate.h
src/object/crl.c [new file with mode: 0644]
src/object/crl.h [new file with mode: 0644]
src/object/manifest.c
src/object/manifest.h
src/object/roa.c
src/object/roa.h
src/object/signed_object.c
src/object/signed_object.h
src/state.c [new file with mode: 0644]
src/state.h [new file with mode: 0644]

index c3b4faf2a4310e04a2c8f2db0ec927e37b1eef17..92a6c2fac21aaccea00f2fa487b7cacaf0cc56ed 100644 (file)
@@ -4,12 +4,14 @@ LDFLAGS_DEBUG = -rdynamic
 
 bin_PROGRAMS = rpki_validator
 
-rpki_validator_SOURCES  = main2.c
+rpki_validator_SOURCES  = main.c
 
 rpki_validator_SOURCES += common.h common.c
 rpki_validator_SOURCES += debug.h debug.c
 rpki_validator_SOURCES += file.h file.c
 rpki_validator_SOURCES += line_file.h line_file.c
+rpki_validator_SOURCES += log.h log.c
+rpki_validator_SOURCES += state.h state.c
 
 rpki_validator_SOURCES += asn1/content_info.h asn1/content_info.c
 rpki_validator_SOURCES += asn1/decode.h asn1/decode.c
@@ -17,6 +19,7 @@ rpki_validator_SOURCES += asn1/oid.h asn1/oid.c
 rpki_validator_SOURCES += asn1/signed_data.h asn1/signed_data.c
 
 rpki_validator_SOURCES += object/certificate.h object/certificate.c
+rpki_validator_SOURCES += object/crl.h object/crl.c
 rpki_validator_SOURCES += object/manifest.h object/manifest.c
 rpki_validator_SOURCES += object/roa.h object/roa.c
 rpki_validator_SOURCES += object/signed_object.h object/signed_object.c
index a9de4350722ad52baea517d9e344cbace17d9c71..d363d789741cd15d21d2dc939d87448cc3c2725a 100644 (file)
@@ -1,6 +1,5 @@
 #include "content_info.h"
 
-#include <err.h>
 #include <errno.h>
 #include <libcmscodec/ContentType.h>
 #include "file.h"
@@ -29,12 +28,13 @@ validate(struct ContentInfo *info)
 }
 
 static int
-decode(struct file_contents *fc, struct ContentInfo **result)
+decode(struct validation *state, struct file_contents *fc,
+    struct ContentInfo **result)
 {
        struct ContentInfo *cinfo;
        int error;
 
-       error = asn1_decode_fc(fc, &asn_DEF_ContentInfo, (void **) &cinfo);
+       error = asn1_decode_fc(state, fc, &asn_DEF_ContentInfo, (void **) &cinfo);
        if (error)
                return error;
 
@@ -49,16 +49,17 @@ decode(struct file_contents *fc, struct ContentInfo **result)
 }
 
 int
-content_info_load(const char *file_name, struct ContentInfo **result)
+content_info_load(struct validation *state, const char *file_name,
+    struct ContentInfo **result)
 {
        struct file_contents fc;
        int error;
 
-       error = file_load(file_name, &fc);
+       error = file_load(state, file_name, &fc);
        if (error)
                return error;
 
-       error = decode(&fc, result);
+       error = decode(state, &fc, result);
 
        file_free(&fc);
        return error;
index 87c7e61987d73f1e1dc5aa518ab970de8179eb7a..b8f91d9dc070288c0aa79885b08a47d4f6f3efb4 100644 (file)
@@ -4,8 +4,9 @@
 /* Some wrappers for libcmscodec's ContentInfo. */
 
 #include <libcmscodec/ContentInfo.h>
+#include "state.h"
 
-int content_info_load(const char *file_name, struct ContentInfo **result);
-void content_info_free(struct ContentInfo *info);
+int content_info_load(struct validation *, const char *, struct ContentInfo **);
+void content_info_free(struct ContentInfo *);
 
 #endif /* SRC_CONTENT_INFO_H_ */
index 0e6c2a93c106f58030843748685a70c192c7bba4..4abd49baacee7d9d95ac49a01c49e73209a05991 100644 (file)
@@ -1,11 +1,12 @@
 #include "decode.h"
 
-#include <err.h>
 #include <errno.h>
 #include "common.h"
+#include "log.h"
 
 static int
-validate(asn_TYPE_descriptor_t const *descriptor, void *result)
+validate(struct validation *state, asn_TYPE_descriptor_t const *descriptor,
+    void *result)
 {
        char error_msg[256];
        size_t error_msg_size;
@@ -16,7 +17,7 @@ validate(asn_TYPE_descriptor_t const *descriptor, void *result)
        error = asn_check_constraints(descriptor, result, error_msg,
            &error_msg_size);
        if (error == -1) {
-               warnx("Error validating ASN.1 object: %s", error_msg);
+               pr_err(state, "Error validating ASN.1 object: %s", error_msg);
                return -EINVAL;
        }
 
@@ -24,7 +25,7 @@ validate(asn_TYPE_descriptor_t const *descriptor, void *result)
 }
 
 int
-asn1_decode(const void *buffer, size_t buffer_size,
+asn1_decode(struct validation *state, const void *buffer, size_t buffer_size,
     asn_TYPE_descriptor_t const *descriptor, void **result)
 {
        asn_dec_rval_t rval;
@@ -35,13 +36,13 @@ asn1_decode(const void *buffer, size_t buffer_size,
        rval = ber_decode(0, descriptor, result, buffer, buffer_size);
        if (rval.code != RC_OK) {
                /* TODO if rval.code == RC_WMORE (1), more work is needed */
-               warnx("Error decoding ASN.1 object: %d", rval.code);
+               pr_err(state, "Error decoding ASN.1 object: %d", rval.code);
                /* Must free partial object according to API contracts. */
                ASN_STRUCT_FREE(*descriptor, *result);
                return -EINVAL;
        }
 
-       error = validate(descriptor, *result);
+       error = validate(state, descriptor, *result);
        if (error) {
                ASN_STRUCT_FREE(*descriptor, *result);
                return error;
@@ -51,25 +52,24 @@ asn1_decode(const void *buffer, size_t buffer_size,
 }
 
 int
-asn1_decode_any(ANY_t *any,
-    asn_TYPE_descriptor_t const *descriptor,
-    void **result)
+asn1_decode_any(struct validation *state, ANY_t *any,
+    asn_TYPE_descriptor_t const *descriptor, void **result)
 {
-       return asn1_decode(any->buf, any->size, descriptor, result);
+       return asn1_decode(state, any->buf, any->size, descriptor, result);
 }
 
 int
-asn1_decode_octet_string(OCTET_STRING_t *string,
-    asn_TYPE_descriptor_t const *descriptor,
-    void **result)
+asn1_decode_octet_string(struct validation *state, OCTET_STRING_t *string,
+    asn_TYPE_descriptor_t const *descriptor, void **result)
 {
-       return asn1_decode(string->buf, string->size, descriptor, result);
+       return asn1_decode(state, string->buf, string->size, descriptor,
+           result);
 }
 
 int
-asn1_decode_fc(struct file_contents *fc,
-    asn_TYPE_descriptor_t const *descriptor,
-    void **result)
+asn1_decode_fc(struct validation *state, struct file_contents *fc,
+    asn_TYPE_descriptor_t const *descriptor, void **result)
 {
-       return asn1_decode(fc->buffer, fc->buffer_size, descriptor, result);
+       return asn1_decode(state, fc->buffer, fc->buffer_size, descriptor,
+           result);
 }
index e20f878d86bf52b7f67549d871a4504ab203d7a5..7c68ed5aae9eeaf5e93dcab2deac3d28f95b33e0 100644 (file)
@@ -4,12 +4,15 @@
 #include <libcmscodec/ANY.h>
 #include <libcmscodec/constr_TYPE.h>
 #include "file.h"
+#include "state.h"
 
-int asn1_decode(const void *, size_t, asn_TYPE_descriptor_t const *, void **);
-int asn1_decode_any(ANY_t *, asn_TYPE_descriptor_t const *, void **);
-int asn1_decode_octet_string(OCTET_STRING_t *, asn_TYPE_descriptor_t const *,
-    void **);
-int asn1_decode_fc(struct file_contents *, asn_TYPE_descriptor_t const *,
+int asn1_decode(struct validation *, const void *, size_t,
+    asn_TYPE_descriptor_t const *, void **);
+int asn1_decode_any(struct validation *, ANY_t *, asn_TYPE_descriptor_t const *,
     void **);
+int asn1_decode_octet_string(struct validation *, OCTET_STRING_t *,
+    asn_TYPE_descriptor_t const *, void **);
+int asn1_decode_fc(struct validation *, struct file_contents *,
+    asn_TYPE_descriptor_t const *, void **);
 
 #endif /* SRC_ASN1_DECODE_H_ */
index 6089ba934497b6e26984dcec4f86ffeb0186cfce..031ce113b2a5ba7109fe854b58a072692c46e0bd 100644 (file)
@@ -51,12 +51,12 @@ oid2arcs(OBJECT_IDENTIFIER_t *oid, struct oid_arcs *result)
 
 /* Callers must free @result. */
 int
-any2arcs(ANY_t *any, struct oid_arcs *result)
+any2arcs(struct validation *state, ANY_t *any, struct oid_arcs *result)
 {
        OBJECT_IDENTIFIER_t *oid;
        int error;
 
-       error = asn1_decode_any(any, &asn_DEF_OBJECT_IDENTIFIER,
+       error = asn1_decode_any(state, any, &asn_DEF_OBJECT_IDENTIFIER,
            (void **) &oid);
        if (error)
                return error;
index 8dbc630bcae968e39f19750f814378af5cddedd2..d31b3b4c405de3f42c167410d941eac0b38df746 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include <libcmscodec/AlgorithmIdentifier.h>
 #include "common.h"
+#include "state.h"
 
 /* These objects are expected to live on the stack. */
 struct oid_arcs {
@@ -38,7 +39,7 @@ typedef asn_oid_arc_t OID[];
 #define OID_SHA512                   { 2, 16, 840, 1, 101, 3, 4, 2, 3 }
 
 int oid2arcs(OBJECT_IDENTIFIER_t *, struct oid_arcs *);
-int any2arcs(ANY_t *, struct oid_arcs *);
+int any2arcs(struct validation *, ANY_t *, struct oid_arcs *);
 
 bool arcs_equal(struct oid_arcs const *, struct oid_arcs const *);
 /* Use ARCS_EQUAL_OID() instead. */
index 9d20ef90503d293160666ac3ac70874350751878..731084a0fa94f06c95467867a5c6897d4280471e 100644 (file)
@@ -1,8 +1,8 @@
 #include "signed_data.h"
 
-#include <err.h>
 #include <errno.h>
 #include <libcmscodec/ContentType.h>
+#include "log.h"
 #include "oid.h"
 #include "asn1/decode.h"
 
@@ -41,8 +41,8 @@ is_digest_algorithm(AlgorithmIdentifier_t *aid, bool *result)
 }
 
 static int
-validate_content_type_attribute(CMSAttributeValue_t *value,
-    EncapsulatedContentInfo_t *eci)
+validate_content_type_attribute(struct validation *state,
+    CMSAttributeValue_t *value, EncapsulatedContentInfo_t *eci)
 {
        struct oid_arcs attrValue_arcs;
        struct oid_arcs EncapContentInfo_arcs;
@@ -50,7 +50,7 @@ validate_content_type_attribute(CMSAttributeValue_t *value,
 
        /* rfc6488#section-3.1.h */
 
-       error = any2arcs(value, &attrValue_arcs);
+       error = any2arcs(state, value, &attrValue_arcs);
        if (error)
                return error;
 
@@ -61,7 +61,8 @@ validate_content_type_attribute(CMSAttributeValue_t *value,
        }
 
        if (!arcs_equal(&attrValue_arcs, &EncapContentInfo_arcs)) {
-               warnx("The eContentType in the EncapsulatedContentInfo does not match the attrValues in the content-type attribute.");
+               pr_err(state,
+                   "The eContentType in the EncapsulatedContentInfo does not match the attrValues in the content-type attribute.");
                error = -EINVAL;
        }
 
@@ -71,13 +72,14 @@ validate_content_type_attribute(CMSAttributeValue_t *value,
 }
 
 static int
-validate_message_digest_attribute(CMSAttributeValue_t *value)
+validate_message_digest_attribute(struct validation *state, CMSAttributeValue_t *value)
 {
        return 0; /* TODO need the content being signed */
 }
 
 static int
-validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
+validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
+    EncapsulatedContentInfo_t *eci)
 {
        struct CMSAttribute *attr;
        struct CMSAttribute__attrValues *attrs;
@@ -90,25 +92,28 @@ validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
        int error;
 
        if (sinfo->signedAttrs == NULL) {
-               warnx("The SignerInfo's signedAttrs field is NULL.");
+               pr_err(state, "The SignerInfo's signedAttrs field is NULL.");
                return -EINVAL;
        }
 
        for (i = 0; i < sinfo->signedAttrs->list.count; i++) {
                attr = sinfo->signedAttrs->list.array[i];
                if (attr == NULL) {
-                       warnx("SignedAttrs array element %u is NULL.", i);
+                       pr_err(state, "SignedAttrs array element %u is NULL.",
+                           i);
                        continue;
                }
                attrs = &attr->attrValues;
 
                if (attrs->list.count != 1) {
-                       warnx("signedAttrs's attribute set size (%d) is different than 1.",
+                       pr_err(state, 0,
+                           "signedAttrs's attribute set size (%d) is different than 1.",
                            attr->attrValues.list.count);
                        return -EINVAL;
                }
                if (attrs->list.array == NULL || attrs->list.array[0] == NULL) {
-                       warnx("Programming error: Array size is 1 but array itself is NULL.");
+                       pr_err(state,
+                           "Programming error: Array size is 1 but array itself is NULL.");
                        return -EINVAL;
                }
 
@@ -118,23 +123,25 @@ validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
 
                if (ARCS_EQUAL_OIDS(&attrType, oid_cta)) {
                        if (content_type_found) {
-                               warnx("Multiple ContentTypes found.");
+                               pr_err(state, "Multiple ContentTypes found.");
                                goto illegal_attrType;
                        }
-                       error = validate_content_type_attribute(attr->attrValues.list.array[0], eci);
+                       error = validate_content_type_attribute(state,
+                           attr->attrValues.list.array[0], eci);
                        content_type_found = true;
 
                } else if (ARCS_EQUAL_OIDS(&attrType, oid_mda)) {
                        if (message_digest_found) {
-                               warnx("Multiple MessageDigests found.");
+                               pr_err(state, "Multiple MessageDigests found.");
                                goto illegal_attrType;
                        }
-                       error = validate_message_digest_attribute(attr->attrValues.list.array[0]);
+                       error = validate_message_digest_attribute(state,
+                           attr->attrValues.list.array[0]);
                        message_digest_found = true;
 
                } else if (ARCS_EQUAL_OIDS(&attrType, oid_sta)) {
                        if (signing_time_found) {
-                               warnx("Multiple SigningTimes found.");
+                               pr_err(state, "Multiple SigningTimes found.");
                                goto illegal_attrType;
                        }
                        error = 0; /* No validations needed for now. */
@@ -142,7 +149,7 @@ validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
 
                } else if (ARCS_EQUAL_OIDS(&attrType, oid_bst)) {
                        if (binary_signing_time_found) {
-                               warnx("Multiple BinarySigningTimes found.");
+                               pr_err(state, "Multiple BinarySigningTimes found.");
                                goto illegal_attrType;
                        }
                        error = 0; /* No validations needed for now. */
@@ -150,7 +157,7 @@ validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
 
                } else {
                        /* rfc6488#section-3.1.g */
-                       warnx("Illegal attrType OID in SignerInfo.");
+                       pr_err(state, "Illegal attrType OID in SignerInfo.");
                        goto illegal_attrType;
                }
 
@@ -162,11 +169,11 @@ validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
 
        /* rfc6488#section-3.1.f */
        if (!content_type_found) {
-               warnx("SignerInfo lacks a ContentType attribute.");
+               pr_err(state, "SignerInfo lacks a ContentType attribute.");
                return -EINVAL;
        }
        if (!message_digest_found) {
-               warnx("SignerInfo lacks a MessageDigest attribute.");
+               pr_err(state, "SignerInfo lacks a MessageDigest attribute.");
                return -EINVAL;
        }
 
@@ -178,7 +185,7 @@ illegal_attrType:
 }
 
 static int
-validate(struct SignedData *sdata)
+validate(struct validation *state, struct SignedData *sdata)
 {
        struct SignerInfo *sinfo;
        bool is_digest;
@@ -186,7 +193,8 @@ validate(struct SignedData *sdata)
 
        /* rfc6488#section-2.1 */
        if (sdata->signerInfos.list.count != 1) {
-               warnx("The SignedData's SignerInfo set is supposed to have only one element. (%d given.)",
+               pr_err(state,
+                   "The SignedData's SignerInfo set is supposed to have only one element. (%d given.)",
                    sdata->signerInfos.list.count);
                return -EINVAL;
        }
@@ -194,7 +202,8 @@ validate(struct SignedData *sdata)
        /* rfc6488#section-2.1.1 */
        /* rfc6488#section-3.1.b */
        if (sdata->version != 3) {
-               warnx("The SignedData version is only allowed to be 3. (Was %ld.)",
+               pr_err(state,
+                   "The SignedData version is only allowed to be 3. (Was %ld.)",
                    sdata->version);
                return -EINVAL;
        }
@@ -202,7 +211,8 @@ validate(struct SignedData *sdata)
        /* rfc6488#section-2.1.2 */
        /* rfc6488#section-3.1.j 1/2 */
        if (sdata->digestAlgorithms.list.count != 1) {
-               warnx("The SignedData's digestAlgorithms set is supposed to have only one element. (%d given.)",
+               pr_err(state,
+                   "The SignedData's digestAlgorithms set is supposed to have only one element. (%d given.)",
                    sdata->digestAlgorithms.list.count);
                return -EINVAL;
        }
@@ -218,7 +228,8 @@ validate(struct SignedData *sdata)
        if (error)
                return error;
        if (!is_digest) {
-               warnx("The SignedData's digestAlgorithm OID is not listed in RFC 5754.");
+               pr_err(state,
+                   "The SignedData's digestAlgorithm OID is not listed in RFC 5754.");
                return -EINVAL;
        }
 
@@ -228,12 +239,13 @@ validate(struct SignedData *sdata)
        /* rfc6488#section-2.1.4 */
        /* rfc6488#section-3.1.c TODO missing half of the requirement. */
        if (sdata->certificates == NULL) {
-               warnx("The SignedData does not contain certificates.");
+               pr_err(state, "The SignedData does not contain certificates.");
                return -EINVAL;
        }
 
        if (sdata->certificates->list.count != 1) {
-               warnx("The SignedData contains %d certificates, one expected.",
+               pr_err(state,
+                   "The SignedData contains %d certificates, one expected.",
                    sdata->certificates->list.count);
                return -EINVAL;
        }
@@ -241,7 +253,7 @@ validate(struct SignedData *sdata)
        /* rfc6488#section-2.1.5 */
        /* rfc6488#section-3.1.d */
        if (sdata->crls != NULL && sdata->crls->list.count > 0) {
-               warnx("The SignedData contains at least one crls.");
+               pr_err(state, "The SignedData contains at least one crls.");
                return -EINVAL;
        }
 
@@ -249,11 +261,12 @@ validate(struct SignedData *sdata)
        /* rfc6488#section-3.1.e */
        sinfo = sdata->signerInfos.list.array[0];
        if (sinfo == NULL) {
-               warnx("The SignerInfo object is NULL.");
+               pr_err(state, "The SignerInfo object is NULL.");
                return -EINVAL;
        }
        if (sinfo->version != 3) {
-               warnx("The SignerInfo version is only allowed to be 3. (Was %ld.)",
+               pr_err(state,
+                   "The SignerInfo version is only allowed to be 3. (Was %ld.)",
                    sinfo->version);
                return -EINVAL;
        }
@@ -270,12 +283,13 @@ validate(struct SignedData *sdata)
        if (error)
                return error;
        if (!is_digest) {
-               warnx("The SignerInfo digestAlgorithm OID is not listed in RFC 5754.");
+               pr_err(state,
+                   "The SignerInfo digestAlgorithm OID is not listed in RFC 5754.");
                return -EINVAL;
        }
 
        /* rfc6488#section-2.1.6.4 */
-       error = validate_signed_attrs(sinfo, &sdata->encapContentInfo);
+       error = validate_signed_attrs(state, sinfo, &sdata->encapContentInfo);
        if (error)
                return error;
 
@@ -303,7 +317,7 @@ validate(struct SignedData *sdata)
        /* rfc6488#section-2.1.6.7 */
        /* rfc6488#section-3.1.i */
        if (sinfo->unsignedAttrs != NULL && sinfo->unsignedAttrs->list.count > 0) {
-               warnx("SignerInfo has at least one unsignedAttr.");
+               pr_err(state, "SignerInfo has at least one unsignedAttr.");
                return -EINVAL;
        }
 
@@ -315,17 +329,19 @@ validate(struct SignedData *sdata)
 }
 
 int
-signed_data_decode(ANY_t *coded, struct SignedData **result)
+signed_data_decode(struct validation *state, ANY_t *coded,
+    struct SignedData **result)
 {
        struct SignedData *sdata;
        int error;
 
        /* rfc6488#section-3.1.l TODO this is BER, not guaranteed to be DER. */
-       error = asn1_decode_any(coded, &asn_DEF_SignedData, (void **) &sdata);
+       error = asn1_decode_any(state, coded, &asn_DEF_SignedData,
+           (void **) &sdata);
        if (error)
                return error;
 
-       error = validate(sdata);
+       error = validate(state, sdata);
        if (error) {
                signed_data_free(sdata);
                return error;
@@ -343,7 +359,8 @@ signed_data_free(struct SignedData *sdata)
 
 /* Caller must free *@result. */
 int
-get_content_type_attr(struct SignedData *sdata, OBJECT_IDENTIFIER_t **result)
+get_content_type_attr(struct validation *state, struct SignedData *sdata,
+    OBJECT_IDENTIFIER_t **result)
 {
        struct SignedAttributes *signedAttrs;
        struct CMSAttribute *attr;
@@ -377,8 +394,10 @@ get_content_type_attr(struct SignedData *sdata, OBJECT_IDENTIFIER_t **result)
                                return -EINVAL;
                        if (attr->attrValues.list.array[0] == NULL)
                                return -EINVAL;
-                       return asn1_decode_any(attr->attrValues.list.array[0],
-                           &asn_DEF_OBJECT_IDENTIFIER, (void **) result);
+                       return asn1_decode_any(state,
+                           attr->attrValues.list.array[0],
+                           &asn_DEF_OBJECT_IDENTIFIER,
+                           (void **) result);
                }
        }
 
index f35d2c6b55cf4ee22afe41ab192b196252633084..6d21361ef28d88d33efb1b648e7deac832b89098 100644 (file)
@@ -4,10 +4,12 @@
 /* Some wrappers for libcmscodec's SignedData. */
 
 #include <libcmscodec/SignedData.h>
+#include "state.h"
 
-int signed_data_decode(ANY_t *, struct SignedData **);
+int signed_data_decode(struct validation *, ANY_t *, struct SignedData **);
 void signed_data_free(struct SignedData *);
 
-int get_content_type_attr(struct SignedData *, OBJECT_IDENTIFIER_t **);
+int get_content_type_attr(struct validation *, struct SignedData *,
+    OBJECT_IDENTIFIER_t **);
 
 #endif /* SRC_SIGNED_DATA_H_ */
index 493878192ef37df00232593efd59a174e78688b0..9dbd2e6272a167b3f397d62df092ca9899d49eb8 100644 (file)
@@ -2,42 +2,14 @@
 
 #include <err.h>
 #include <errno.h>
-
-#define INDENT_MAX 10
-static unsigned int indent;
+#include <string.h>
+#include <openssl/err.h>
+#include "log.h"
 
 char const *repository;
 int NID_rpkiManifest;
 int NID_rpkiNotify;
 
-void
-pr_indent(void)
-{
-       unsigned int __indent = indent;
-       unsigned int i;
-
-//     if (__indent > INDENT_MAX)
-//             __indent = INDENT_MAX;
-
-       for (i = 0; i < __indent; i++)
-               printf("  ");
-}
-
-void
-pr_add_indent(void)
-{
-       indent++;
-}
-
-void
-pr_rm_indent(void)
-{
-       if (indent > 0)
-               indent--;
-       else
-               warnx("Programming error: Too many pr_sub_indent()s.");
-}
-
 bool
 file_has_extension(char const *file_name, char const *extension)
 {
@@ -55,7 +27,7 @@ file_has_extension(char const *file_name, char const *extension)
  * You need to free the result once you're done.
  */
 int
-uri_g2l(char const *guri, char **result)
+uri_g2l(struct validation *state, char const *guri, char **result)
 {
        char const *const PREFIX = "rsync://";
        char *luri;
@@ -66,7 +38,13 @@ uri_g2l(char const *guri, char **result)
        prefix_len = strlen(PREFIX);
 
        if (strncmp(PREFIX, guri, prefix_len) != 0) {
-               warnx("Global URI %s does not begin with '%s'.", guri, PREFIX);
+               if (state == NULL) {
+                       warnx("Global URI %s does not begin with '%s'.", guri,
+                           PREFIX);
+               } else {
+                       pr_err(state, "Global URI %s does not begin with '%s'.",
+                           guri, PREFIX);
+               }
                return -EINVAL;
        }
 
@@ -91,14 +69,14 @@ uri_g2l(char const *guri, char **result)
 }
 
 int
-gn2uri(GENERAL_NAME *gn, char const **uri)
+gn2uri(struct validation *state, GENERAL_NAME *gn, char const **uri)
 {
        ASN1_STRING *asn_string;
        int type;
 
        asn_string = GENERAL_NAME_get0_value(gn, &type);
        if (type != GEN_URI) {
-               pr_debug("Unknown GENERAL_NAME type: %d", type);
+               pr_debug(state, "Unknown GENERAL_NAME type: %d", type);
                return -ENOTSUPPORTED;
        }
 
index d37cb306ec42afe5ae0697a86105b89cfd19c806..409eef955dc48dc018522a2a613ae9ffe9e2e0e7 100644 (file)
@@ -2,9 +2,8 @@
 #define SRC_RTR_COMMON_H_
 
 #include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
 #include <openssl/x509v3.h>
+#include "state.h"
 
 /* "I think that this is not supposed to be implemented." */
 #define ENOTSUPPORTED 3172
@@ -17,62 +16,8 @@ extern int NID_rpkiNotify;
 
 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
 
-#define warnxerror0(error, msg) \
-       warnx(msg ": %s", strerror(error))
-#define warnxerrno0(msg) \
-       warnxerror0(errno, msg)
-#define warnxerror(error, msg, ...) \
-       warnx(msg ": %s", ##__VA_ARGS__, strerror(error))
-#define warnxerrno(msg, ...) \
-       warnxerror(errno, msg, ##__VA_ARGS__)
-
-#ifdef DEBUG
-
-#define pr_debug(msg, ...) {                   \
-       printf("DBG: ");                        \
-       pr_indent();                            \
-       printf(msg "\n", ##__VA_ARGS__);        \
-}
-#define pr_debug_add(msg, ...) {               \
-       pr_debug(msg, ##__VA_ARGS__);           \
-       pr_add_indent();                        \
-}
-#define pr_debug_rm(msg, ...) {                        \
-       pr_rm_indent();                         \
-       pr_debug(msg, ##__VA_ARGS__);           \
-}
-
-#define pr_debug0(msg) {                       \
-       printf("DBG: ");                        \
-       pr_indent();                            \
-       printf(msg "\n");                       \
-}
-#define pr_debug0_add(msg) {                   \
-       pr_debug0(msg);                         \
-       pr_add_indent();                        \
-}
-#define pr_debug0_rm(msg) {                    \
-       pr_rm_indent();                         \
-       pr_debug0(msg);                         \
-}
-
-#else /* #ifdef DEBUG */
-
-#define pr_debug(msg, ...)
-#define pr_debug_add(msg, ...)
-#define pr_debug_rm(msg, ...)
-#define pr_debug0(msg)
-#define pr_debug0_add(msg)
-#define pr_debug0_rm(msg)
-
-#endif /* #ifdef DEBUG */
-
-void pr_indent(void);
-void pr_add_indent(void);
-void pr_rm_indent(void);
-
 bool file_has_extension(char const *, char const *);
-int uri_g2l(char const *guri, char **result);
-int gn2uri(GENERAL_NAME *, char const **);
+int uri_g2l(struct validation *, char const *, char **);
+int gn2uri(struct validation *, GENERAL_NAME *, char const **);
 
 #endif /* SRC_RTR_COMMON_H_ */
index 3f3047b879ef8e571b4cd7c479adf218c30ceeec..310b010be2c91541a28715312b99cda7cd09e241 100644 (file)
@@ -1,10 +1,10 @@
 #include "file.h"
 
-#include <err.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "common.h"
+#include "log.h"
 
 /*
  * Will also rewind the file as a side effect.
@@ -21,7 +21,8 @@ get_file_size(FILE *file, long int *size)
 }
 
 int
-file_load(const char *file_name, struct file_contents *fc)
+file_load(struct validation *state, const char *file_name,
+    struct file_contents *fc)
 {
        FILE *file;
        long int file_size;
@@ -30,14 +31,15 @@ file_load(const char *file_name, struct file_contents *fc)
 
        file = fopen(file_name, "rb");
        if (file == NULL) {
-               warnxerrno("Could not open file '%s'", file_name);
-               return errno;
+               return pr_errno(state, errno, "Could not open file '%s'",
+                   file_name);
        }
 
        /* TODO if @file is a directory, this returns a very large integer. */
        error = get_file_size(file, &file_size);
        if (error) {
-               warnxerror0(error, "Could not compute file size");
+               pr_errno(state, error, "Could not compute the file size of %s",
+                   file_name);
                fclose(file);
                return error;
        }
@@ -45,7 +47,7 @@ file_load(const char *file_name, struct file_contents *fc)
        fc->buffer_size = file_size;
        fc->buffer = malloc(fc->buffer_size);
        if (fc->buffer == NULL) {
-               warnx("Out of memory.");
+               pr_err(state, "Out of memory.");
                fclose(file);
                return -ENOMEM;
        }
@@ -59,8 +61,9 @@ file_load(const char *file_name, struct file_contents *fc)
                         * code. It literally doesn't say how to obtain the
                         * error code.
                         */
-                       warnx("File read error. The errcode is presumably %d. (%s)",
-                           error, strerror(error));
+                       pr_errno(state, error,
+                           "File reading error. Error message (apparently)",
+                           file_name);
                        free(fc->buffer);
                        fclose(file);
                        return error;
@@ -70,9 +73,9 @@ file_load(const char *file_name, struct file_contents *fc)
                 * As far as I can tell from the man page, feof() cannot return
                 * less bytes that requested like read() does.
                 */
-               warnx("Likely programming error: fread() < file size");
-               warnx("fr:%zu bs:%zu EOF:%d", fread_result, fc->buffer_size,
-                               feof(file));
+               pr_err(state, "Likely programming error: fread() < file size");
+               pr_err(state, "fr:%zu bs:%zu EOF:%d", fread_result,
+                   fc->buffer_size, feof(file));
                free(fc->buffer);
                fclose(file);
                return -EINVAL;
index 3ee0aa8c3da29466a50ecf24b5a3972c5f45d2b9..3e4123904ba650301cf3ae98e4393666ad8c7f4e 100644 (file)
@@ -2,6 +2,7 @@
 #define SRC_FILE_H_
 
 #include <stddef.h>
+#include "state.h"
 
 /*
  * The entire contents of the file, loaded into a buffer.
@@ -13,7 +14,7 @@ struct file_contents {
        size_t buffer_size;
 };
 
-int file_load(const char *file_name, struct file_contents *fc);
-void file_free(struct file_contents *fc);
+int file_load(struct validation *, const char *, struct file_contents *);
+void file_free(struct file_contents *);
 
 #endif /* SRC_FILE_H_ */
index e723c1e95b078acfc732590d312aba782e359ff1..ea2aee7a8c66b3dcb741950377f9b70c37896a94 100644 (file)
@@ -106,7 +106,7 @@ lfile_read(struct line_file *lfile, char **result)
                if (feof(lfile->file))
                        return 0;
                warnx("Supposedly unreachable code reached. ferror:%d feof:%d",
-                               ferror(lfile->file), feof(lfile->file));
+                   ferror(lfile->file), feof(lfile->file));
                return -EINVAL;
        }
 
@@ -120,7 +120,7 @@ lfile_read(struct line_file *lfile, char **result)
        for (i = 0; i < len; i++) {
                if (string[i] == '\0') {
                        warnx("File '%s' has an illegal null character in its body. Please remove it.",
-                                       lfile_name(lfile));
+                           lfile_name(lfile));
                        free(string);
                        return -EINVAL;
                }
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..7919d8d
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,192 @@
+#include "log.h"
+
+#include <string.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+
+#ifdef DEBUG
+
+#define INDENT_MAX 10
+static unsigned int indent;
+
+static void
+pr_indent(BIO *stream)
+{
+       unsigned int __indent = indent;
+       unsigned int i;
+
+//     if (__indent > INDENT_MAX)
+//             __indent = INDENT_MAX;
+
+       for (i = 0; i < __indent; i++)
+               BIO_printf(stream, "  ");
+}
+
+static void
+pr_add_indent(void)
+{
+       indent++;
+}
+
+static void
+pr_rm_indent(void)
+{
+       if (indent > 0)
+               indent--;
+       else
+               fprintf(stderr, "Programming error: Too many pr_rm_indent()s.\n");
+}
+
+static void
+print_debug_prefix(BIO *bio)
+{
+       BIO_printf(bio, "DBG: ");
+       pr_indent(bio);
+}
+
+#endif
+
+void
+pr_debug(struct validation *state, const char *format, ...)
+{
+#ifdef DEBUG
+       BIO *bio = validation_stdout(state);
+       va_list args;
+
+       print_debug_prefix(bio);
+
+       va_start(args, format);
+       BIO_vprintf(bio, format, args);
+       va_end(args);
+       BIO_printf(bio, "\n");
+#endif
+}
+
+void
+pr_debug_add(struct validation *state, const char *format, ...)
+{
+#ifdef DEBUG
+       BIO *bio = validation_stdout(state);
+       va_list args;
+
+       print_debug_prefix(bio);
+
+       va_start(args, format);
+       BIO_vprintf(bio, format, args);
+       va_end(args);
+       BIO_printf(bio, "\n");
+
+       pr_add_indent();
+#endif
+}
+
+void
+pr_debug_rm(struct validation *state, const char *format, ...)
+{
+#ifdef DEBUG
+       BIO *bio = validation_stdout(state);
+       va_list args;
+
+       pr_rm_indent();
+
+       print_debug_prefix(bio);
+
+       va_start(args, format);
+       BIO_vprintf(bio, format, args);
+       va_end(args);
+       BIO_printf(bio, "\n");
+#endif
+}
+
+/**
+ * Always appends a newline at the end.
+ */
+void
+pr_err(struct validation *state, const char *format, ...)
+{
+       BIO *bio = validation_stderr(state);
+       va_list args;
+
+       va_start(args, format);
+       BIO_vprintf(bio, format, args);
+       va_end(args);
+       BIO_printf(bio, "\n");
+}
+
+/**
+ * @error fulfills two functions, both of which apply only if it's nonzero:
+ *
+ * - @error's corresponding generic error message will be appended to the print.
+ * - @error's value will be returned. This is for the sake of error code
+ *   propagation.
+ *
+ * If @error is zero, no error message will be appended, and the function will
+ * return -EINVAL. (I acknowledge that this looks convoluted at first glance.
+ * The purpose is to ensure that this function will propagate an error code even
+ * if there is no error code originally.)
+ *
+ * Always appends a newline at the end.
+ */
+int
+pr_errno(struct validation *state, int error, const char *format, ...)
+{
+       BIO *bio = validation_stderr(state);
+       va_list args;
+
+       va_start(args, format);
+       BIO_vprintf(bio, format, args);
+       va_end(args);
+
+       if (error) {
+               BIO_printf(bio, ": %s", strerror(error));
+       } else {
+               /* We should assume that there WAS an error; go generic. */
+               error = -EINVAL;
+       }
+
+       BIO_printf(bio, "\n");
+
+       return error;
+}
+
+/**
+ * This is like pr_err() and pr_errno(), except meant to log an error made
+ * during a libcrypto routine.
+ *
+ * This differs from usual printf-like functions:
+ *
+ * - It returns the last error code libcrypto threw, not bytes written.
+ * - It prints a newline.
+ * - Also prints the cryptolib's error message after a colon.
+ *   (So don't include periods at the end of @format.)
+ *
+ * Always appends a newline at the end.
+ */
+int
+crypto_err(struct validation *state, const char *format, ...)
+{
+       BIO *bio = validation_stderr(state);
+       va_list args;
+       int error;
+
+       error = ERR_GET_REASON(ERR_peek_last_error());
+
+       va_start(args, format);
+       BIO_vprintf(bio, format, args);
+       va_end(args);
+
+       if (error) {
+               BIO_printf(bio, ": ");
+               /*
+                * Reminder: This clears the error queue.
+                * BTW: The string format is pretty ugly. Maybe override this.
+                */
+               ERR_print_errors(bio);
+       } else {
+               /* We should assume that there WAS an error; go generic. */
+               error = -EINVAL;
+       }
+
+       BIO_printf(bio, "\n");
+       return error;
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..6697054
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,17 @@
+#ifndef SRC_LOG_H_
+#define SRC_LOG_H_
+
+#include "state.h"
+
+void pr_debug(struct validation *, const char *, ...);
+void pr_debug_add(struct validation *, const char *, ...);
+void pr_debug_rm(struct validation *, const char *, ...);
+
+void pr_err(struct validation *, const char *, ...);
+int pr_errno(struct validation *, int, const char *, ...);
+int crypto_err(struct validation *, const char *, ...);
+
+#define PR_DEBUG(msg) \
+    printf("%s:%d (%s()): " msg "\n", __FILE__, __LINE__, __func__)
+
+#endif /* SRC_LOG_H_ */
similarity index 67%
rename from src/main2.c
rename to src/main.c
index c3e1a2c3432a8f7cd78ea8325b8b410bee384b10..467b742b3020d450e1c913278bbcfea330eeb4e9 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "common.h"
 #include "debug.h"
+#include "log.h"
 #include "object/certificate.h"
 #include "object/manifest.h"
 #include "object/tal.h"
@@ -19,12 +20,12 @@ add_rpki_oids(void)
        NID_rpkiManifest = OBJ_create("1.3.6.1.5.5.7.48.10",
            "id-ad-rpkiManifest (RFC 6487)",
            "Resource Public Key Infrastructure (RPKI) manifest access method");
-       pr_debug("rpkiManifest registered. Its nid is %d.", NID_rpkiManifest);
+       printf("rpkiManifest registered. Its nid is %d.\n", NID_rpkiManifest);
 
        NID_rpkiNotify = OBJ_create("1.3.6.1.5.5.7.48.13",
            "id-ad-rpkiNotify (RFC 8182)",
            /* TODO */ "Blah blah");
-       pr_debug("rpkiNotify registered. Its nid is %d.", NID_rpkiNotify);
+       printf("rpkiNotify registered. Its nid is %d.\n", NID_rpkiNotify);
 }
 
 /**
@@ -34,27 +35,35 @@ add_rpki_oids(void)
 static int
 handle_tal_uri(char const *uri)
 {
+       struct validation *state;
        char *cert_file;
        int error;
 
-       pr_debug_add("TAL URI %s {", uri);
+       error = uri_g2l(NULL, uri, &cert_file);
+       if (error)
+               return error;
+
+       error = validation_create(&state, cert_file);
+       if (error)
+               goto end1;
+
+       pr_debug_add(state, "TAL URI %s {", uri);
 
        if (!is_certificate(uri)) {
-               warnx("TAL file does not seem to point to a certificate.");
-               warnx("(Expected .cer file, got '%s')", uri);
+               pr_err(state,
+                   "TAL file does not point to a certificate. (Expected .cer, got '%s')",
+                   uri);
                error = -ENOTSUPPORTED;
-               goto end;
+               goto end2;
        }
 
-       error = uri_g2l(uri, &cert_file);
-       if (error)
-               goto end;
+       error = certificate_handle_extensions(state, validation_peek(state));
 
-       error = handle_certificate(cert_file);
+end2:
+       pr_debug_rm(state, "}");
+       validation_destroy(state);
+end1:
        free(cert_file);
-
-end:
-       pr_debug0_rm("}");
        return error;
 }
 
@@ -64,6 +73,8 @@ main(int argc, char **argv)
        struct tal *tal;
        int error;
 
+       print_stack_trace_on_segfault();
+
        if (argc < 3) {
                warnx("Repository path as first argument and TAL file as second argument, please.");
                return -EINVAL;
index a09ee6e124324df0deb2e21f2b6468635e2b8b89..48be031669c326884483cb7c14e5621adf69d9a2 100644 (file)
@@ -1,12 +1,12 @@
 #include "certificate.h"
 
-#include <err.h>
 #include <libcmscodec/SubjectInfoAccessSyntax.h>
 #include <openssl/err.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
 #include "common.h"
+#include "log.h"
 #include "manifest.h"
 #include "asn1/decode.h"
 
@@ -22,37 +22,33 @@ bool is_certificate(char const *file_name)
        return file_has_extension(file_name, "cer");
 }
 
-static X509 *
-load_certificate(BIO *bio_err, const char *file)
+X509 *
+certificate_load(struct validation *state, const char *file)
 {
        X509 *cert = NULL;
        BIO *bio;
 
        bio = BIO_new(BIO_s_file());
        if (bio == NULL) {
-               BIO_printf(bio_err, "BIO_new(BIO_s_file()) returned NULL.\n");
-               ERR_print_errors(bio_err);
+               crypto_err(state, "BIO_new(BIO_s_file()) returned NULL");
                goto end;
        }
        if (BIO_read_filename(bio, file) <= 0) {
-               BIO_printf(bio_err, "Error reading certificate '%s'.\n", file);
-               ERR_print_errors(bio_err);
+               crypto_err(state, "Error reading certificate '%s'", file);
                goto end;
        }
 
        cert = d2i_X509_bio(bio, NULL);
-       if (cert == NULL) {
-               BIO_printf(bio_err, "Error parsing certificate '%s'.\n", file);
-               ERR_print_errors(bio_err);
-       }
+       if (cert == NULL)
+               crypto_err(state, "Error parsing certificate '%s'", file);
 
 end:
        BIO_free(bio);
        return cert;
 }
 
-static int
-handle_extensions(X509 *cert)
+int
+certificate_handle_extensions(struct validation *state, X509 *cert)
 {
        SIGNATURE_INFO_ACCESS *sia;
        ACCESS_DESCRIPTION *ad;
@@ -63,22 +59,23 @@ handle_extensions(X509 *cert)
        int error;
 
        sia = X509_get_ext_d2i(cert, NID_sinfo_access, &error, NULL);
-       if (!sia) {
+       if (sia == NULL) {
                switch (error) {
                case -1:
-                       warnx("The certificate lacks an SIA extension.");
+                       pr_err(state, "Certificate lacks an SIA extension.");
                        return -ESRCH;
                case -2:
-                       warnx("The certificate has more than one SIA extension.");
+                       pr_err(state, "Certificate has more than one SIA extension.");
                        return -EINVAL;
                default:
-                       warnx("X509_get_ext_d2i() returned unknown error code %d.",
+                       pr_err(state,
+                           "X509_get_ext_d2i() returned unknown error code %d.",
                            error);
                        return -EINVAL;
                }
        }
 
-       pr_debug0_add("SIA {");
+       pr_debug_add(state, "SIA {");
        error = 0;
 
        for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
@@ -86,70 +83,63 @@ handle_extensions(X509 *cert)
                nid = OBJ_obj2nid(ad->method);
 
                if (nid == NID_rpkiManifest) {
-                       error = gn2uri(ad->location, &uri);
+                       error = gn2uri(state, ad->location, &uri);
                        if (error)
                                goto end;
-                       error = uri_g2l(uri, &luri);
+                       error = uri_g2l(state, uri, &luri);
                        if (error)
                                goto end;
-                       error = handle_manifest(luri);
+                       error = handle_manifest(state, luri);
                        free(luri);
                        if (error)
                                goto end;
 
                } else if (nid == NID_rpkiNotify) {
                        /* TODO Another fucking RFC... */
-                       pr_debug0("Unimplemented thingy: rpkiNotify");
+                       pr_debug(state, "Unimplemented thingy: rpkiNotify");
 
                } else if (nid == NID_caRepository) {
-                       error = gn2uri(ad->location, &uri);
+                       error = gn2uri(state, ad->location, &uri);
                        if (error)
                                goto end;
                        /* TODO no idea what to do with this. */
-                       pr_debug("CA Repository URI: %s", uri);
+                       pr_debug(state, "CA Repository URI: %s", uri);
 
                } else {
-                       pr_debug("Unknown NID: %d", nid);
+                       pr_debug(state, "Unknown NID: %d", nid);
                        goto end;
                }
        }
 
 end:
        AUTHORITY_INFO_ACCESS_free(sia);
-       pr_debug0_rm("}");
+       pr_debug_rm(state, "}");
        return error;
 }
 
 int
-handle_certificate(char const *file)
+certificate_handle(struct validation *state, char const *file)
 {
        X509 *certificate;
-       BIO *bio_err;
        int error;
 
-       pr_debug0_add("Certificate {");
-
-       bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
-       if (bio_err == NULL) {
-               warnx("Failed to initialise bio_err.");
-               /* TODO get the right one through the ERR_* functions. */
-               error = -ENOMEM;
-               goto abort1;
-       }
+       pr_debug_add(state, "Certificate {");
 
-       certificate = load_certificate(bio_err, file);
-       if (!certificate) {
+       certificate = certificate_load(state, file);
+       if (certificate == NULL) {
                /* TODO get the right one through the ERR_* functions. */
                error = -EINVAL;
-               goto abort2;
+               goto end;
        }
 
-       error = handle_extensions(certificate);
-       X509_free(certificate);
+       error = validation_push(state, certificate);
+       if (error)
+               goto end;
+
+       error = certificate_handle_extensions(state, certificate);
+       validation_pop(state);
 
-abort2:
-       BIO_free_all(bio_err);
-abort1:
-       pr_debug0_rm("}");
+end:
+       pr_debug_rm(state, "}");
        return error;
 }
index 9e89fc243c3b5001b23d95587d70c467fc35d1f5..fc75c83cf48b23498932a6dc91fb900b8bcea587 100644 (file)
@@ -2,8 +2,11 @@
 #define SRC_OBJECT_CERTIFICATE_H_
 
 #include <stdbool.h>
+#include "state.h"
 
 bool is_certificate(char const *);
-int handle_certificate(char const *);
+X509 *certificate_load(struct validation *, const char *);
+int certificate_handle(struct validation *, char const *);
+int certificate_handle_extensions(struct validation *, X509 *);
 
 #endif /* SRC_OBJECT_CERTIFICATE_H_ */
diff --git a/src/object/crl.c b/src/object/crl.c
new file mode 100644 (file)
index 0000000..a86c30c
--- /dev/null
@@ -0,0 +1,164 @@
+#include "crl.h"
+
+#include <libcmscodec/SubjectInfoAccessSyntax.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "common.h"
+#include "log.h"
+#include "manifest.h"
+#include "asn1/decode.h"
+
+bool is_crl(char const *file_name)
+{
+       return file_has_extension(file_name, "crl");
+}
+
+static X509_CRL *
+load_crl(struct validation *state, const char *file)
+{
+       X509_CRL *crl = NULL;
+       BIO *bio;
+
+       bio = BIO_new(BIO_s_file());
+       if (bio == NULL) {
+               crypto_err(state, "BIO_new(BIO_s_file()) returned NULL");
+               goto end;
+       }
+       if (BIO_read_filename(bio, file) <= 0) {
+               crypto_err(state, "Error reading CRL '%s'", file);
+               goto end;
+       }
+
+       crl = d2i_X509_CRL_bio(bio, NULL);
+       if (crl == NULL)
+               crypto_err(state, "Error parsing CRL '%s'", file);
+
+end:
+       BIO_free(bio);
+       return crl;
+}
+
+static int
+handle_authority_key_identifier(struct validation *state, X509_EXTENSION *ext)
+{
+       /* TODO */
+       pr_debug(state, "Unimplemented still: Authority Key Identifier");
+       /* AUTHORITY_KEYID *aki = X509V3_EXT_d2i(ext); */
+       /* AUTHORITY_KEYID_free(aki); */
+       return 0;
+}
+
+static int
+handle_revoked(struct validation *state, X509_REVOKED *revoked)
+{
+       const ASN1_INTEGER *serialNumber;
+       const ASN1_TIME *revocationDate;
+
+       serialNumber = X509_REVOKED_get0_serialNumber(revoked);
+       revocationDate = X509_REVOKED_get0_revocationDate(revoked);
+
+       if (serialNumber == NULL) {
+               pr_err(state, "Revoked entry's serial number is NULL.");
+               return -EINVAL;
+       }
+       if (revocationDate == NULL) {
+               pr_err(state, "Revoked entry's revocation date is NULL.");
+               return -EINVAL;
+       }
+       if (X509_REVOKED_get0_extensions(revoked) != NULL) {
+               pr_err(state, "Revoked entry's extension list is not NULL.");
+               return -EINVAL;
+       }
+
+       pr_debug(state, "Revoked:%ld", ASN1_INTEGER_get(serialNumber));
+//     ASN1_TIME_print(bio_err, revocationDate);
+//     printf("\n");
+       return 0;
+}
+
+static int
+handle_revoked_list(struct validation *state, X509_CRL *crl)
+{
+       STACK_OF(X509_REVOKED) *list;
+       unsigned int i;
+       int error;
+
+       list = X509_CRL_get_REVOKED(crl);
+       for (i = 0; i < sk_X509_REVOKED_num(list); i++) {
+               error = handle_revoked(state, sk_X509_REVOKED_value(list, i));
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+static int
+handle_crl_number(struct validation *state, X509_EXTENSION *ext)
+{
+       pr_debug(state, "Unimplemented still: CRL Number"); /* TODO */
+       return 0;
+}
+
+static int
+handle_extensions(struct validation *state, X509_CRL *crl)
+{
+       const STACK_OF(X509_EXTENSION) *exts;
+       X509_EXTENSION *ext;
+       unsigned int i;
+       int nid;
+       int error;
+
+       exts = X509_CRL_get0_extensions(crl);
+       for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
+               ext = sk_X509_EXTENSION_value(exts, i);
+               nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
+
+               switch (nid) {
+               case NID_authority_key_identifier:
+                       error = handle_authority_key_identifier(state, ext);
+                       break;
+               case NID_crl_number:
+                       error = handle_crl_number(state, ext);
+                       break;
+               default:
+                       pr_err(state, "CRL has illegal extension: NID %d", nid);
+                       return -EINVAL;
+               }
+
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+int
+handle_crl(struct validation *state, char const *file)
+{
+       X509_CRL *crl;
+       int error;
+
+       pr_debug_add(state, "CRL {");
+
+       crl = load_crl(state, file);
+       if (!crl) {
+               /* TODO get the right one through the ERR_* functions. */
+               error = -EINVAL;
+               goto abort2;
+       }
+
+       error = handle_revoked_list(state, crl);
+       if (error)
+               goto abort3;
+
+       error = handle_extensions(state, crl);
+
+abort3:
+       X509_CRL_free(crl);
+abort2:
+       pr_debug_rm(state, "}");
+       return error;
+}
diff --git a/src/object/crl.h b/src/object/crl.h
new file mode 100644 (file)
index 0000000..b1a99bf
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef SRC_OBJECT_CRL_H_
+#define SRC_OBJECT_CRL_H_
+
+#include <stdbool.h>
+#include "state.h"
+
+bool is_crl(char const *);
+int handle_crl(struct validation *, char const *);
+
+#endif /* SRC_OBJECT_CRL_H_ */
index 8bc791014fa06dae2f28a2fd3d8befea9c9eb2d8..87b90748f2f7730a86f57955c50356665e55f577 100644 (file)
@@ -4,11 +4,14 @@
 #include <libcmscodec/Manifest.h>
 
 #include "common.h"
+#include "log.h"
 #include "asn1/oid.h"
 #include "object/certificate.h"
+#include "object/crl.h"
 #include "object/roa.h"
 #include "object/signed_object.h"
 
+/* TODO not being called right now. */
 bool
 is_manifest(char const *file_name)
 {
@@ -153,39 +156,44 @@ succeed:
 }
 
 static int
-handle_file(char const *mft, IA5String_t *string)
+handle_file(struct validation *state, char const *mft, IA5String_t *string)
 {
        char *luri;
        int error;
 
        /* TODO Treating string->buf as a C string is probably not correct. */
-       pr_debug_add("File %s {", string->buf);
+//     pr_debug_add(state, "File %s {", string->buf);
 
        error = get_relative_file(mft, (char const *) string->buf, &luri);
        if (error)
                goto end;
 
+       pr_debug_add(state, "File %s {", luri);
+
        if (is_certificate(luri))
-               error = handle_certificate(luri);
+               error = certificate_handle(state, luri);
+       else if (is_crl(luri))
+               error = handle_crl(state, luri);
        else if (is_roa(luri))
-               error = handle_roa(luri);
+               error = handle_roa(state, luri);
        else
-               pr_debug0("Unhandled file type.");
+               pr_debug(state, "Unhandled file type.");
 
        free(luri);
 end:
-       pr_debug0_rm("}");
+       pr_debug_rm(state, "}");
        return error;
 }
 
 static int
-__handle_manifest(char const *mft, struct Manifest *manifest)
+__handle_manifest(struct validation *state, char const *mft,
+    struct Manifest *manifest)
 {
        int i;
        int error;
 
        for (i = 0; i < manifest->fileList.list.count; i++) {
-               error = handle_file(mft,
+               error = handle_file(state, mft,
                    &manifest->fileList.list.array[i]->file);
                if (error)
                        return error;
@@ -195,21 +203,21 @@ __handle_manifest(char const *mft, struct Manifest *manifest)
 }
 
 int
-handle_manifest(char const *file_path)
+handle_manifest(struct validation *state, char const *file_path)
 {
        static OID oid = OID_MANIFEST;
        struct oid_arcs arcs = OID2ARCS(oid);
        struct Manifest *manifest;
        int error;
 
-       error = signed_object_decode(file_path, &asn_DEF_Manifest, &arcs,
+       error = signed_object_decode(state, file_path, &asn_DEF_Manifest, &arcs,
            (void **) &manifest);
        if (error)
                return error;
 
        error = validate_manifest(manifest);
        if (!error)
-               error = __handle_manifest(file_path, manifest);
+               error = __handle_manifest(state, file_path, manifest);
 
        ASN_STRUCT_FREE(asn_DEF_Manifest, manifest);
        return error;
index 00156fa4c76b70d631a0986316f9f4fa7947146c..e957c4c2e55ec420677596b6938fe32032581483 100644 (file)
@@ -2,8 +2,9 @@
 #define SRC_OBJECT_MANIFEST_H_
 
 #include <stdbool.h>
+#include "state.h"
 
 bool is_manifest(char const *);
-int handle_manifest(char const *);
+int handle_manifest(struct validation *, char const *);
 
 #endif /* SRC_OBJECT_MANIFEST_H_ */
index c2a4a7feec6180e0ad1bbc217c2063205bdd42c4..9c6e5b13d445bcd66c1d92d8adb28b5e925ece26 100644 (file)
@@ -1,11 +1,11 @@
 #include "object/roa.h"
 
-#include <err.h>
 #include <errno.h>
 #include <arpa/inet.h>
 #include <libcmscodec/RouteOriginAttestation.h>
 
 #include "common.h"
+#include "log.h"
 #include "asn1/oid.h"
 #include "object/signed_object.h"
 
@@ -15,11 +15,11 @@ bool is_roa(char const *file_name)
 }
 
 static int
-validate_roa(struct RouteOriginAttestation *roa)
+validate_roa(struct validation *state, struct RouteOriginAttestation *roa)
 {
        /* rfc6482#section-3.1 */
        if (roa->version != 0) {
-               warnx("ROA's version (%ld) is not zero.", roa->version);
+               pr_err(state, "ROA's version (%ld) is nonzero.", roa->version);
                return -EINVAL;
        }
 
@@ -31,7 +31,8 @@ validate_roa(struct RouteOriginAttestation *roa)
 }
 
 static int
-print_addr(long asn, uint8_t family, struct ROAIPAddress *roa_addr)
+print_addr(struct validation *state, long asn, uint8_t family,
+    struct ROAIPAddress *roa_addr)
 {
        union {
                struct in6_addr ip6;
@@ -43,7 +44,6 @@ print_addr(long asn, uint8_t family, struct ROAIPAddress *roa_addr)
        } str;
        int prefix_len;
        const char *str2;
-       int error;
 
        switch (family) {
        case 1:
@@ -53,7 +53,7 @@ print_addr(long asn, uint8_t family, struct ROAIPAddress *roa_addr)
                family = AF_INET6;
                break;
        default:
-               warnx("Unknown family value: %u", family);
+               pr_err(state, "Unknown family value: %u", family);
                return -EINVAL;
        }
 
@@ -68,11 +68,8 @@ print_addr(long asn, uint8_t family, struct ROAIPAddress *roa_addr)
        memset(&addr, 0, sizeof(addr));
        memcpy(&addr, roa_addr->address.buf, roa_addr->address.size);
        str2 = inet_ntop(family, &addr, str.ip6, sizeof(str));
-       if (str2 == NULL) {
-               error = errno;
-               warnxerrno0("Could not parse IP address");
-               return error;
-       }
+       if (str2 == NULL)
+               return pr_errno(state, errno, "Cannot parse IP address");
 
        prefix_len = 8 * roa_addr->address.size - roa_addr->address.bits_unused;
 
@@ -88,7 +85,7 @@ print_addr(long asn, uint8_t family, struct ROAIPAddress *roa_addr)
 }
 
 static int
-__handle_roa(struct RouteOriginAttestation *roa)
+__handle_roa(struct validation *state, struct RouteOriginAttestation *roa)
 {
        struct ROAIPAddressFamily *block;
        int b;
@@ -111,7 +108,7 @@ __handle_roa(struct RouteOriginAttestation *roa)
                if (block->addresses.list.array == NULL)
                        return -EINVAL;
                for (a = 0; a < block->addresses.list.count; a++) {
-                       error = print_addr(roa->asID,
+                       error = print_addr(state, roa->asID,
                            block->addressFamily.buf[1],
                            block->addresses.list.array[a]);
                        if (error)
@@ -122,21 +119,21 @@ __handle_roa(struct RouteOriginAttestation *roa)
        return 0;
 }
 
-int handle_roa(char const *file)
+int handle_roa(struct validation *state, char const *file)
 {
        static OID oid = OID_ROA;
        struct oid_arcs arcs = OID2ARCS(oid);
        struct RouteOriginAttestation *roa;
        int error;
 
-       error = signed_object_decode(file, &asn_DEF_RouteOriginAttestation,
-           &arcs, (void **) &roa);
+       error = signed_object_decode(state, file,
+           &asn_DEF_RouteOriginAttestation, &arcs, (void **) &roa);
        if (error)
                return error;
 
-       error = validate_roa(roa);
+       error = validate_roa(state, roa);
        if (!error)
-               error = __handle_roa(roa);
+               error = __handle_roa(state, roa);
 
        ASN_STRUCT_FREE(asn_DEF_RouteOriginAttestation, roa);
        return error;
index 9795c397322d755394e957e01e1266e0966a35d6..a2efda4741331c1047b4997abf47fb4913cd21a0 100644 (file)
@@ -2,8 +2,9 @@
 #define SRC_OBJECT_ROA_H_
 
 #include <stdbool.h>
+#include "state.h"
 
 bool is_roa(char const *);
-int handle_roa(char const *);
+int handle_roa(struct validation *, char const *);
 
 #endif /* SRC_OBJECT_ROA_H_ */
index 3fe3062d6598a296be9c347fa2534ab21422bdae..d162e76742b58cae5230eadf74be6ce27f2acbab 100644 (file)
@@ -1,13 +1,14 @@
 #include "signed_object.h"
 
-#include <err.h>
 #include <errno.h>
+#include "log.h"
 #include "asn1/content_info.h"
 #include "asn1/decode.h"
 #include "asn1/signed_data.h"
 
 static int
-validate_eContentType(struct SignedData *sdata,
+validate_eContentType(struct validation *state,
+    struct SignedData *sdata,
     asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid)
 {
@@ -21,7 +22,8 @@ validate_eContentType(struct SignedData *sdata,
        equals = arcs_equal(&arcs, oid);
        free_arcs(&arcs);
        if (!equals) {
-               warnx("SignedObject's encapContentInfo lacks the OID of a %s.",
+               pr_err(state,
+                   "SignedObject's encapContentInfo lacks the OID of a %s.",
                    descriptor->name);
                return -EINVAL;
        }
@@ -30,7 +32,8 @@ validate_eContentType(struct SignedData *sdata,
 }
 
 static int
-validate_content_type(struct SignedData *sdata,
+validate_content_type(struct validation *state,
+    struct SignedData *sdata,
     asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid)
 {
@@ -39,7 +42,7 @@ validate_content_type(struct SignedData *sdata,
        bool equals;
        int error;
 
-       error = get_content_type_attr(sdata, &ctype);
+       error = get_content_type_attr(state, sdata, &ctype);
        if (error)
                return error;
        error = oid2arcs(ctype, &arcs);
@@ -49,7 +52,8 @@ validate_content_type(struct SignedData *sdata,
        equals = arcs_equal(&arcs, oid);
        free_arcs(&arcs);
        if (!equals) {
-               warnx("SignedObject's content type attribute lacks the OID of a %s.",
+               pr_err(state,
+                   "SignedObject's content type attribute lacks the OID of a %s.",
                    descriptor->name);
                return -EINVAL;
        }
@@ -58,7 +62,8 @@ validate_content_type(struct SignedData *sdata,
 }
 
 int
-signed_object_decode(char const *file,
+signed_object_decode(struct validation *state,
+    char const *file,
     asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid,
     void **result)
@@ -67,29 +72,29 @@ signed_object_decode(char const *file,
        struct SignedData *sdata;
        int error;
 
-       error = content_info_load(file, &cinfo);
+       error = content_info_load(state, file, &cinfo);
        if (error)
                goto end1;
 
-       error = signed_data_decode(&cinfo->content, &sdata);
+       error = signed_data_decode(state, &cinfo->content, &sdata);
        if (error)
                goto end2;
 
        /* rfc6482#section-2 */
        /* rfc6486#section-4.1 */
        /* rfc6486#section-4.4.1 */
-       error = validate_eContentType(sdata, descriptor, oid);
+       error = validate_eContentType(state, sdata, descriptor, oid);
        if (error)
                goto end3;
 
        /* rfc6482#section-2 */
        /* rfc6486#section-4.3 */
-       error = validate_content_type(sdata, descriptor, oid);
+       error = validate_content_type(state, sdata, descriptor, oid);
        if (error)
                goto end3;
 
-       error = asn1_decode_octet_string(sdata->encapContentInfo.eContent,
-           descriptor, result);
+       error = asn1_decode_octet_string(state,
+           sdata->encapContentInfo.eContent, descriptor, result);
 
 end3:  signed_data_free(sdata);
 end2:  content_info_free(cinfo);
index 9311d1f62337cd3280fe5afdb0833b46bfdd8d04..7bc49088dfbd27d4f4a3eb0c865b2e243af4f28a 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "asn1/oid.h"
 
-int signed_object_decode(char const *, asn_TYPE_descriptor_t const *,
-    struct oid_arcs const *, void **);
+int signed_object_decode(struct validation *, char const *,
+    asn_TYPE_descriptor_t const *, struct oid_arcs const *, void **);
 
 #endif /* SRC_OBJECT_SIGNED_OBJECT_H_ */
diff --git a/src/state.c b/src/state.c
new file mode 100644 (file)
index 0000000..3485db5
--- /dev/null
@@ -0,0 +1,255 @@
+#include "state.h"
+
+#include <errno.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include "common.h"
+#include "log.h"
+#include "object/certificate.h"
+
+/**
+ * The current state of the validation cycle.
+ *
+ * It is one of the core objects in this project. Every time a trust anchor
+ * triggers a validation cycle, the validator creates one of these objects and
+ * uses it to traverse the tree and keep track of validated data.
+ */
+struct validation {
+       /**
+        * Encapsulated standard output.
+        * Needed because the crypto library won't write to stdout directly.
+        */
+       BIO *out;
+       /**
+        * Encapsulated standard error.
+        * Needed because the crypto library won't write to stderr directly.
+        */
+       BIO *err;
+
+       /** https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_load_locations.html */
+       X509_STORE *store;
+
+       /** Certificates we've already validated. */
+       STACK_OF(X509) *trusted;
+};
+
+/*
+ * It appears that this function is called by LibreSSL whenever it finds an
+ * error while validating.
+ * It is expected to return "okay" status: Nonzero if the error should be
+ * ignored, zero if the error is grounds to abort the validation.
+ *
+ * Note to myself: During my tests, this function was called in
+ * X509_verify_cert(ctx) -> check_chain_extensions(0, ctx),
+ * and then twice again in
+ * X509_verify_cert(ctx) -> internal_verify(1, ctx).
+ *
+ * Regarding the ok argument: I'm not 100% sure that I get it; I don't
+ * understand why this function would be called with ok = 1.
+ * http://openssl.cs.utah.edu/docs/crypto/X509_STORE_CTX_set_verify_cb.html
+ * The logic I implemented is the same as the second example: Always ignore the
+ * error that's troubling the library, otherwise try to be as unintrusive as
+ * possible.
+ */
+static int
+cb(int ok, X509_STORE_CTX *ctx)
+{
+       int error;
+
+       /*
+        * We need to handle two new critical extensions (IP Resources and ASN
+        * Resources), so unknown critical extensions are fine as far as
+        * LibreSSL is concerned.
+        * Unfortunately, LibreSSL has no way of telling us *which* is the
+        * unknown critical extension, but since RPKI defines its own set of
+        * valid extensions, we'll have to figure it out later anyway.
+        */
+       error = X509_STORE_CTX_get_error(ctx);
+       return (error == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) ? 1 : ok;
+}
+
+static int
+init_trusted(struct validation *result, char *root)
+{
+       X509 *cert;
+       int ok;
+       int error;
+
+       cert = certificate_load(result, root);
+       if (cert == NULL)
+               return -EINVAL;
+
+       result->trusted = sk_X509_new_null();
+       if (result->trusted == NULL) {
+               error = -EINVAL;
+               goto abort1;
+       }
+
+       ok = sk_X509_push(result->trusted, cert);
+       if (ok <= 0) {
+               error = crypto_err(result,
+                   "Could not add certificate to trusted stack: %d", ok);
+               goto abort2;
+       }
+
+       return 0;
+
+abort2:
+       sk_X509_free(result->trusted);
+abort1:
+       X509_free(cert);
+       return error;
+}
+
+int
+validation_create(struct validation **out, char *root)
+{
+       struct validation *result;
+       int error = -ENOMEM;
+
+       result = malloc(sizeof(struct validation));
+       if (!result)
+               return -ENOMEM;
+
+       result->out = BIO_new_fp(stdout, BIO_NOCLOSE);
+       if (result->out == NULL) {
+               fprintf(stderr, "Failed to initialise standard output's BIO.\n");
+               goto abort1;
+       }
+       result->err = BIO_new_fp(stderr, BIO_NOCLOSE);
+       if (result->out == NULL) {
+               fprintf(stderr, "Failed to initialise standard error's BIO.\n");
+               goto abort2;
+       }
+
+       result->store = X509_STORE_new();
+       if (!result->store) {
+               error = crypto_err(result, "X509_STORE_new() returned NULL");
+               goto abort3;
+       }
+
+       X509_STORE_set_verify_cb(result->store, cb);
+
+       error = init_trusted(result, root);
+       if (error)
+               goto abort4;
+
+       *out = result;
+       return 0;
+
+abort4:
+       X509_STORE_free(result->store);
+abort3:
+       BIO_free_all(result->err);
+abort2:
+       BIO_free_all(result->out);
+abort1:
+       free(result);
+       return error;
+}
+
+void
+validation_destroy(struct validation *state)
+{
+       int cert_num;
+
+       /*
+        * Only the certificate created during validation_create() should
+        * remain.
+        */
+       cert_num = sk_X509_num(state->trusted);
+       if (cert_num != 1) {
+               pr_err(state, "Error: validation state has %d certificates. (1 expected)",
+                   cert_num);
+       }
+
+       sk_X509_pop_free(state->trusted, X509_free);
+       X509_STORE_free(state->store);
+       BIO_free_all(state->err);
+       BIO_free_all(state->out);
+       free(state);
+}
+
+/**
+ * "Swallows" @cert; do not delete it.
+ */
+int
+validation_push(struct validation *state, X509 *cert)
+{
+       /*
+        * TODO
+        * The only difference between -CAfile and -trusted, as it seems, is
+        * that -CAfile consults the default file location, while -trusted does
+        * not. As far as I can tell, this means that we absolutely need to use
+        * -trusted.
+        * So, just in case, enable -no-CAfile and -no-CApath.
+        */
+
+       X509_STORE_CTX *ctx;
+       int ok;
+
+       ctx = X509_STORE_CTX_new();
+       if (ctx == NULL) {
+               crypto_err(state, "X509_STORE_CTX_new() returned NULL");
+               goto end1;
+       }
+
+       /* Returns 0 or 1 , all callers test ! only. */
+       ok = X509_STORE_CTX_init(ctx, state->store, cert, NULL);
+       if (!ok) {
+               crypto_err(state, "X509_STORE_CTX_init() returned %d", ok);
+               goto end2;
+       }
+
+       X509_STORE_CTX_trusted_stack(ctx, state->trusted);
+
+       /* Can return negative codes, all callers do <= 0. */
+       ok = X509_verify_cert(ctx);
+       if (ok <= 0) {
+               crypto_err(state, "Certificate validation failed: %d", ok);
+               goto end2;
+       }
+
+       /* Returns number of stack elements or 0 */
+       ok = sk_X509_push(state->trusted, cert);
+       if (ok <= 0) {
+               crypto_err(state,
+                   "Could not add certificate to trusted stack: %d", ok);
+               goto end2;
+       }
+
+       X509_STORE_CTX_free(ctx);
+       return 0;
+
+end2:
+       X509_STORE_CTX_free(ctx);
+end1:
+       X509_free(cert);
+       return -EINVAL;
+}
+
+void
+validation_pop(struct validation *state)
+{
+       X509 *cert = sk_X509_pop(state->trusted);
+       X509_free(cert);
+}
+
+X509 *
+validation_peek(struct validation *state)
+{
+       return sk_X509_value(state->trusted, sk_X509_num(state->trusted) - 1);
+}
+
+BIO *
+validation_stdout(struct validation *state)
+{
+       return state->out;
+}
+
+BIO *
+validation_stderr(struct validation *state)
+{
+       return state->err;
+}
diff --git a/src/state.h b/src/state.h
new file mode 100644 (file)
index 0000000..52218b5
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef SRC_STATE_H_
+#define SRC_STATE_H_
+
+#include <openssl/bio.h>
+
+struct validation;
+
+int validation_create(struct validation **, char *);
+void validation_destroy(struct validation *);
+
+int validation_push(struct validation *, X509 *);
+void validation_pop(struct validation *);
+X509 *validation_peek(struct validation *);
+
+BIO *validation_stdout(struct validation *);
+BIO *validation_stderr(struct validation *);
+
+#endif /* SRC_STATE_H_ */