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
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
#include "content_info.h"
-#include <err.h>
#include <errno.h>
#include <libcmscodec/ContentType.h>
#include "file.h"
}
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;
}
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;
/* 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_ */
#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;
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;
}
}
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;
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;
}
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);
}
#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_ */
/* 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;
#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 {
#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. */
#include "signed_data.h"
-#include <err.h>
#include <errno.h>
#include <libcmscodec/ContentType.h>
+#include "log.h"
#include "oid.h"
#include "asn1/decode.h"
}
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;
/* rfc6488#section-3.1.h */
- error = any2arcs(value, &attrValue_arcs);
+ error = any2arcs(state, value, &attrValue_arcs);
if (error)
return error;
}
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;
}
}
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;
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;
}
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. */
} 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. */
} else {
/* rfc6488#section-3.1.g */
- warnx("Illegal attrType OID in SignerInfo.");
+ pr_err(state, "Illegal attrType OID in SignerInfo.");
goto illegal_attrType;
}
/* 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;
}
}
static int
-validate(struct SignedData *sdata)
+validate(struct validation *state, struct SignedData *sdata)
{
struct SignerInfo *sinfo;
bool is_digest;
/* 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;
}
/* 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;
}
/* 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;
}
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;
}
/* 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;
}
/* 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;
}
/* 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;
}
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;
/* 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;
}
}
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;
/* 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;
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);
}
}
/* 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_ */
#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)
{
* 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;
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;
}
}
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;
}
#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
#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_ */
#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.
}
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;
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;
}
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;
}
* 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;
* 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;
#define SRC_FILE_H_
#include <stddef.h>
+#include "state.h"
/*
* The entire contents of the file, loaded into a buffer.
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_ */
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;
}
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;
}
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */
#include "common.h"
#include "debug.h"
+#include "log.h"
#include "object/certificate.h"
#include "object/manifest.h"
#include "object/tal.h"
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);
}
/**
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;
}
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;
#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"
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;
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++) {
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;
}
#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_ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */
#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)
{
}
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;
}
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;
#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_ */
#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"
}
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;
}
}
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;
} str;
int prefix_len;
const char *str2;
- int error;
switch (family) {
case 1:
family = AF_INET6;
break;
default:
- warnx("Unknown family value: %u", family);
+ pr_err(state, "Unknown family value: %u", family);
return -EINVAL;
}
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;
}
static int
-__handle_roa(struct RouteOriginAttestation *roa)
+__handle_roa(struct validation *state, struct RouteOriginAttestation *roa)
{
struct ROAIPAddressFamily *block;
int b;
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)
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;
#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_ */
#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)
{
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;
}
}
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)
{
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);
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;
}
}
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)
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);
#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_ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */