# Run this file to generate the configure script.
# You'll need Autoconf and Automake installed!
-aclocal && automake --add-missing --copy && autoconf
+autoreconf --install
# Checks for library functions.
AC_FUNC_MALLOC
AC_CHECK_FUNCS([memset socket])
-AC_SEARCH_LIBS([pthread_create], [pthread])
+AC_SEARCH_LIBS([pthread_create], [pthread], [],
+ [AC_MSG_ERROR([unable to find the pthread() function])]
+)
+AC_SEARCH_LIBS([ber_decode], [cmscodec], [],
+ [AC_MSG_ERROR([unable to find the ber_decode() function])]
+)
# Check dependencies.
+#PKG_CHECK_MODULES([OPENSSL], [openssl])
PKG_CHECK_MODULES([CHECK], [check])
-PKG_CHECK_MODULES([GLIB], [glib-2.0])
# Spit out the makefiles.
AC_OUTPUT(Makefile src/Makefile test/Makefile)
rpki_validator_SOURCES = main.c
rpki_validator_SOURCES += common.h
-rpki_validator_SOURCES += line_file.h
-rpki_validator_SOURCES += line_file.c
-rpki_validator_SOURCES += tal.h
-rpki_validator_SOURCES += tal.c
+rpki_validator_SOURCES += content_info.h content_info.c
+rpki_validator_SOURCES += file.h file.c
+rpki_validator_SOURCES += oid.h oid.c
+rpki_validator_SOURCES += signed_data.h signed_data.c
-rpki_validator_CFLAGS = -pedantic -Wall -std=gnu11 -O3 ${GLIB_CFLAGS}
-rpki_validator_LDADD = ${GLIB_LIBS}
+rpki_validator_CFLAGS = -pedantic -Wall -std=gnu11 -O0 -g ${OPENSSL_CFLAGS}
+rpki_validator_LDADD = ${OPENSSL_LIBS}
#ifndef SRC_RTR_COMMON_H_
#define SRC_RTR_COMMON_H_
+#include <string.h>
+
#define ARRAY_SIZE(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__)
+
+#define pr_debug(msg, ...) printf("Debug: " msg "\n", ##__VA_ARGS__);
+
#endif /* SRC_RTR_COMMON_H_ */
--- /dev/null
+#include "content_info.h"
+
+#include <err.h>
+#include <errno.h>
+#include <libcmscodec/ContentType.h>
+#include "common.h"
+#include "file.h"
+
+static void
+content_type_print(FILE *stream, asn_oid_arc_t *arcs, unsigned int arc_count)
+{
+ unsigned int i;
+
+ for (i = 0; i < arc_count; i++) {
+ fprintf(stream, "%u", arcs[i]);
+ if (i != arc_count - 1)
+ fprintf(stream, ".");
+ }
+}
+
+int
+content_type_validate(ContentType_t *ctype)
+{
+ asn_oid_arc_t expected[] = { 1, 2, 840, 113549, 1, 7, 2 };
+ asn_oid_arc_t actual[ARRAY_SIZE(expected)];
+ const unsigned int SLOTS = ARRAY_SIZE(expected);
+ ssize_t result;
+ unsigned int i;
+
+ result = OBJECT_IDENTIFIER_get_arcs(ctype, actual, SLOTS);
+ if (result != SLOTS)
+ goto failure;
+
+ for (i = 0; i < SLOTS; i++) {
+ if (expected[i] != actual[i])
+ goto failure;
+ }
+
+ return 0;
+
+failure:
+ fprintf(stderr, "Incorrect content-type; expected ");
+ content_type_print(stderr, expected, SLOTS);
+ fprintf(stderr, ", got ");
+ content_type_print(stderr, actual, (result < SLOTS) ? result : SLOTS);
+ fprintf(stderr, ".\n");
+ return -EINVAL;
+}
+
+static int
+validate(struct ContentInfo *info)
+{
+ char error_msg[256];
+ size_t error_msg_size;
+ int error;
+
+ error_msg_size = sizeof(error_msg);
+ error = asn_check_constraints(&asn_DEF_ContentInfo, info, error_msg,
+ &error_msg_size);
+ if (error == -1) {
+ warnx("Error validating content info object: %s", error_msg);
+ return -EINVAL;
+ }
+
+ return content_type_validate(&info->contentType);
+}
+
+static int
+decode(struct file_contents *fc, struct ContentInfo **result)
+{
+ struct ContentInfo *info = NULL;
+ asn_dec_rval_t rval;
+ int error;
+
+ rval = ber_decode(0, &asn_DEF_ContentInfo, (void **) &info, fc->buffer,
+ fc->buffer_size);
+ if (rval.code != RC_OK) {
+ warnx("Error decoding content info object: %d", rval.code);
+ /* Must free partial content info according to API contracts. */
+ content_info_free(info);
+ return -EINVAL;
+ }
+
+ error = validate(info);
+ if (error) {
+ content_info_free(info);
+ return error;
+ }
+
+ *result = info;
+ return 0;
+}
+
+int
+content_info_load(const char *file_name, struct ContentInfo **result)
+{
+ struct file_contents fc;
+ int error;
+
+ error = file_load(file_name, &fc);
+ if (error)
+ return error;
+
+ error = decode(&fc, result);
+
+ file_free(&fc);
+ return error;
+}
+
+void
+content_info_free(struct ContentInfo *info)
+{
+ asn_DEF_ContentInfo.op->free_struct(&asn_DEF_ContentInfo, info,
+ ASFM_FREE_EVERYTHING);
+}
--- /dev/null
+#ifndef SRC_CONTENT_INFO_H_
+#define SRC_CONTENT_INFO_H_
+
+/* Some wrappers for libcmscodec's ContentInfo. */
+
+#include <libcmscodec/ContentInfo.h>
+
+int content_info_load(const char *file_name, struct ContentInfo **result);
+void content_info_free(struct ContentInfo *info);
+
+#endif /* SRC_CONTENT_INFO_H_ */
--- /dev/null
+#include "file.h"
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+
+/*
+ * Will also rewind the file as a side effect.
+ * This is currently perfect for calling users.
+ */
+static int
+get_file_size(FILE *file, long int *size)
+{
+ if (fseek(file, 0L, SEEK_END) == -1)
+ return errno ? errno : -EINVAL;
+ *size = ftell(file);
+ rewind(file);
+ return 0;
+}
+
+int
+file_load(const char *file_name, struct file_contents *fc)
+{
+ FILE *file;
+ long int file_size;
+ size_t fread_result;
+ int error;
+
+ file = fopen(file_name, "rb");
+ if (file == NULL) {
+ warnxerrno("Could not open file '%s'", file_name);
+ return errno;
+ }
+
+ error = get_file_size(file, &file_size);
+ if (error) {
+ warnxerror0(error, "Could not compute file size");
+ fclose(file);
+ return error;
+ }
+
+ fc->buffer_size = file_size;
+ fc->buffer = malloc(fc->buffer_size);
+ if (fc->buffer == NULL) {
+ warnx("Out of memory.");
+ fclose(file);
+ return -ENOMEM;
+ }
+
+ fread_result = fread(fc->buffer, 1, fc->buffer_size, file);
+ if (fread_result < fc->buffer_size) {
+ error = ferror(file);
+ if (error) {
+ /*
+ * The manpage doesn't say that the result is an error
+ * 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));
+ 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));
+ free(fc->buffer);
+ fclose(file);
+ return -EINVAL;
+ }
+
+ fclose(file);
+ return 0;
+}
+
+void
+file_free(struct file_contents *fc)
+{
+ free(fc->buffer);
+}
--- /dev/null
+#ifndef SRC_FILE_H_
+#define SRC_FILE_H_
+
+#include <stddef.h>
+
+/*
+ * The entire contents of the file, loaded into a buffer.
+ *
+ * Instances of this struct are expected to live on the stack.
+ */
+struct file_contents {
+ unsigned char *buffer;
+ size_t buffer_size;
+};
+
+int file_load(const char *file_name, struct file_contents *fc);
+void file_free(struct file_contents *fc);
+
+#endif /* SRC_FILE_H_ */
*/
for (i = 0; i < len; i++) {
if (string[i] == '\0') {
- warnx("File %s has an illegal null character in its body. Please remove it.",
+ warnx("File '%s' has an illegal null character in its body. Please remove it.",
lfile_name(lfile));
free(string);
return -EINVAL;
-#include <stdlib.h>
+#include "content_info.h"
+#include "signed_data.h"
+
+const char *FILE_NAME = "/home/ydahhrk/rpki-cache/repository/"
+ "ca.rg.net/rpki/RGnet/IZt-j9P0XqJjzM2Xi4RZKS60gOc.roa";
int
main(void)
{
- return EXIT_SUCCESS;
+ struct ContentInfo *cinfo;
+ struct SignedData *sdata;
+ int error;
+
+ error = content_info_load(FILE_NAME, &cinfo);
+ if (error)
+ return error;
+
+ error = signed_data_decode(&cinfo->content, &sdata);
+ if (error) {
+ content_info_free(cinfo);
+ return error;
+ }
+
+// asn_fprint(stdout, &asn_DEF_ContentInfo, cinfo);
+// printf("---------------------------------------------\n");
+// asn_fprint(stdout, &asn_DEF_SignedData, sdata);
+
+ signed_data_free(sdata);
+ content_info_free(cinfo);
+ return 0;
}
--- /dev/null
+#include "oid.h"
+
+#include <errno.h>
+#include "common.h"
+
+#define MAX_ARCS 9
+
+/* Please update MAX_ARCS if you add an OID that has more arcs. */
+static asn_oid_arc_t OID_SHA224[] = { 2, 16, 840, 1, 101, 3, 4, 2, 4 };
+static asn_oid_arc_t OID_SHA256[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 };
+static asn_oid_arc_t OID_SHA384[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 };
+static asn_oid_arc_t OID_SHA512[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 };
+
+/*
+ * @a_oid is the original OID that's being tested.
+ * @a_arcs must be a stack-allocated array of size @len.
+ * @b_arcs is the expected array of arcs that needs to be compared to @a_oid.
+ * Its length must be @len.
+ */
+bool
+oid_equals(OBJECT_IDENTIFIER_t *const actual_oid,
+ asn_oid_arc_t const *expected_arcs,
+ size_t len)
+{
+ asn_oid_arc_t actual_arcs[MAX_ARCS];
+ ssize_t count;
+ long int i;
+
+ count = OBJECT_IDENTIFIER_get_arcs(actual_oid, actual_arcs, len);
+ if (count != len)
+ return false;
+
+ /* Most OIDs start with the same numbers, so iterate backwards. */
+ for (i = len - 1; i >= 0; i--) {
+ if (actual_arcs[i] != expected_arcs[i])
+ return false;
+ }
+
+ return true;
+}
+
+void
+oid_print(OBJECT_IDENTIFIER_t *oid)
+{
+ asn_fprint(stdout, &asn_DEF_OBJECT_IDENTIFIER, oid);
+}
+
+bool
+is_digest_algorithm(AlgorithmIdentifier_t *algorithm)
+{
+ return OID_EQUALS(&algorithm->algorithm, OID_SHA224)
+ || OID_EQUALS(&algorithm->algorithm, OID_SHA256)
+ || OID_EQUALS(&algorithm->algorithm, OID_SHA384)
+ || OID_EQUALS(&algorithm->algorithm, OID_SHA512);
+}
--- /dev/null
+#ifndef SRC_OID_H_
+#define SRC_OID_H_
+
+#include <stdbool.h>
+#include "libcmscodec/AlgorithmIdentifier.h"
+
+#include "common.h"
+
+typedef asn_oid_arc_t OID[];
+
+/* Please update MAX_ARCS if you add an OID that has more arcs. */
+static const OID CONTENT_TYPE_ATTR_OID = {
+ 1, 2, 840, 113549, 1, 9, 3
+};
+static const OID MESSAGE_DIGEST_ATTR_OID = {
+ 1, 2, 840, 113549, 1, 9, 4
+};
+static const OID SIGNING_TIME_ATTR_OID = {
+ 1, 2, 840, 113549, 1, 9, 5
+};
+static const OID BINARY_SIGNING_TIME_ATTR_OID = {
+ 1, 2, 840, 113549, 1, 9, 16, 2, 46
+};
+
+/* Use OID_EQUALS() instead. */
+bool oid_equals(OBJECT_IDENTIFIER_t *const actual_oid,
+ asn_oid_arc_t const *expected_arcs, size_t len);
+
+/*
+ * a is supposed to be a OBJECT_IDENTIFIER_t (from libcmscodec.)
+ * b is supposed to be an OID (from the typedef above.)
+ */
+#define OID_EQUALS(a, b) oid_equals(a, b, ARRAY_SIZE(b))
+
+void oid_print(OBJECT_IDENTIFIER_t *oid);
+
+bool is_digest_algorithm(AlgorithmIdentifier_t *algorithm);
+
+#endif /* SRC_OID_H_ */
--- /dev/null
+#include "signed_data.h"
+
+#include <err.h>
+#include <errno.h>
+#include <libcmscodec/ContentType.h>
+#include "oid.h"
+
+/* TODO more consistent and informative error/warning messages.*/
+
+static int
+validate_content_type_attribute(CMSAttributeValue_t *value,
+ EncapsulatedContentInfo_t *eci)
+{
+ /* TODO need to decode value. */
+
+ /* eci->eContentType*/
+
+ return 0;
+}
+
+static int
+validate_message_digest_attribute(CMSAttributeValue_t *value)
+{
+ return 0; /* TODO need the content being signed */
+}
+
+static int
+validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
+{
+ struct CMSAttribute *attr;
+ struct CMSAttribute__attrValues *attrs;
+ unsigned int i;
+ bool content_type_found = false;
+ bool message_digest_found = false;
+ bool signing_time_found = false;
+ bool binary_signing_time_found = false;
+ int error;
+
+ if (sinfo->signedAttrs == NULL) {
+ warnx("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);
+ continue;
+ }
+ attrs = &attr->attrValues;
+
+ if (attrs->list.count != 1) {
+ warnx("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.");
+ return -EINVAL;
+ }
+
+ if (OID_EQUALS(&attr->attrType, CONTENT_TYPE_ATTR_OID)) {
+ if (content_type_found) {
+ warnx("Multiple ContentTypes found.");
+ return -EINVAL;
+ }
+ error = validate_content_type_attribute(attr->attrValues.list.array[0], eci);
+ content_type_found = true;
+
+ } else if (OID_EQUALS(&attr->attrType, MESSAGE_DIGEST_ATTR_OID)) {
+ if (message_digest_found) {
+ warnx("Multiple MessageDigests found.");
+ return -EINVAL;
+ }
+ error = validate_message_digest_attribute(attr->attrValues.list.array[0]);
+ message_digest_found = true;
+
+ } else if (OID_EQUALS(&attr->attrType, SIGNING_TIME_ATTR_OID)) {
+ if (signing_time_found) {
+ warnx("Multiple SigningTimes found.");
+ return -EINVAL;
+ }
+ error = 0; /* No validations needed for now. */
+ signing_time_found = true;
+
+ } else if (OID_EQUALS(&attr->attrType, BINARY_SIGNING_TIME_ATTR_OID)) {
+ if (binary_signing_time_found) {
+ warnx("Multiple BinarySigningTimes found.");
+ return -EINVAL;
+ }
+ error = 0; /* No validations needed for now. */
+ binary_signing_time_found = true;
+
+ } else {
+ warnx("Illegal attrType OID in SignerInfo.");
+ return -EINVAL;
+ }
+
+ if (error)
+ return error;
+ }
+
+ if (!content_type_found) {
+ warnx("SignerInfo lacks a ContentType attribute.");
+ return -EINVAL;
+ }
+ if (!message_digest_found) {
+ warnx("SignerInfo lacks a MessageDigest attribute.");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+validate(struct SignedData *sdata)
+{
+ char error_msg[256];
+ size_t error_msg_size;
+ int error;
+ struct SignerInfo *sinfo;
+
+ /* The lib's inbuilt validations. (Probably not much.) */
+ error_msg_size = sizeof(error_msg);
+ error = asn_check_constraints(&asn_DEF_SignedData, sdata, error_msg,
+ &error_msg_size);
+ if (error == -1) {
+ warnx("Error validating SignedData object: %s", error_msg);
+ return -EINVAL;
+ }
+
+ /* 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.)",
+ sdata->signerInfos.list.count);
+ return -EINVAL;
+ }
+
+ /* rfc6488#section-2.1.1 */
+ if (sdata->version != 3) {
+ warnx("The SignedData version is only allowed to be 3. (Was %ld.)",
+ sdata->version);
+ return -EINVAL;
+ }
+
+ /* rfc6488#section-2.1.2 */
+ if (sdata->digestAlgorithms.list.count != 1) {
+ warnx("The SignedData's digestAlgorithms set is supposed to have only one element. (%d given.)",
+ sdata->digestAlgorithms.list.count);
+ return -EINVAL;
+ }
+
+ /*
+ * No idea what to do with struct DigestAlgorithmIdentifier; it's not
+ * defined anywhere and the code always seems to fall back to
+ * AlgorithmIdentifier instead. There's no API.
+ * This seems to work fine.
+ */
+ if (!is_digest_algorithm((DigestAlgorithmIdentifier_t *) sdata->digestAlgorithms.list.array[0])) {
+ warnx("The SignedData's digestAlgorithm OID is not listed in RFC 5754.");
+ return -EINVAL;
+ }
+
+ /* section-2.1.3 */
+ /* TODO need a callback for specific signed object types */
+
+ /* rfc6488#section-2.1.4 */
+ if (sdata->certificates == NULL) {
+ warnx("The SignedData does not contain certificates.");
+ return -EINVAL;
+ }
+
+ if (sdata->certificates->list.count != 1) {
+ warnx("The SignedData contains %d certificates, one expected.",
+ sdata->certificates->list.count);
+ return -EINVAL;
+ }
+
+ /* rfc6488#section-2.1.5 */
+ if (sdata->crls != NULL && sdata->crls->list.count > 0) {
+ warnx("The SignedData contains at least one crls.");
+ return -EINVAL;
+ }
+
+ /* rfc6488#section-2.1.6.1 */
+ sinfo = sdata->signerInfos.list.array[0];
+ if (sinfo == NULL) {
+ warnx("The SignerInfo object is NULL.");
+ return -EINVAL;
+ }
+ if (sinfo->version != 3) {
+ warnx("The SignerInfo version is only allowed to be 3. (Was %ld.)",
+ sinfo->version);
+ return -EINVAL;
+ }
+
+ /* rfc6488#section-2.1.6.2 */
+ /*
+ * TODO need the "EE certificate carried in the CMS certificates field."
+ */
+
+ /* rfc6488#section-2.1.6.3 */
+ if (!is_digest_algorithm((AlgorithmIdentifier_t *) &sinfo->digestAlgorithm)) {
+ warnx("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);
+ if (error)
+ return error;
+
+ /* rfc6488#section-2.1.6.5 */
+ /*
+ * RFC 6485 was obsoleted by 7935. 7935 simply refers to 5652.
+ *
+ * RFC 5652:
+ *
+ * > Since each signer can employ a different digital signature
+ * > technique, and future specifications could update the syntax, all
+ * > implementations MUST gracefully handle unimplemented versions of
+ * > SignerInfo. Further, since all implementations will not support
+ * > every possible signature algorithm, all implementations MUST
+ * > gracefully handle unimplemented signature algorithms when they are
+ * > encountered.
+ *
+ * So, nothing to do for now.
+ */
+
+ /* rfc6488#section-2.1.6.6 */
+ /* Again, nothing to do for now. */
+
+ /* rfc6488#section-2.1.6.7 */
+ if (sinfo->unsignedAttrs != NULL && sinfo->unsignedAttrs->list.count > 0) {
+ warnx("SignerInfo has at least one unsignedAttr.");
+ return -EINVAL;
+ }
+
+ /* TODO section 3 */
+
+ return 0;
+}
+
+int
+signed_data_decode(ANY_t *coded, struct SignedData **result)
+{
+ struct SignedData *sdata = NULL;
+ asn_dec_rval_t rval;
+ int error;
+
+ rval = ber_decode(0, &asn_DEF_SignedData, (void **) &sdata, coded->buf,
+ coded->size);
+ if (rval.code != RC_OK) {
+ warnx("Error decoding signed data object: %d", rval.code);
+ /* Must free partial signed data according to API contracts. */
+ signed_data_free(sdata);
+ return -EINVAL;
+ }
+
+ error = validate(sdata);
+ if (error) {
+ signed_data_free(sdata);
+ return error;
+ }
+
+ *result = sdata;
+ return 0;
+}
+
+void
+signed_data_free(struct SignedData *sdata)
+{
+ asn_DEF_SignedData.op->free_struct(&asn_DEF_SignedData, sdata,
+ ASFM_FREE_EVERYTHING);
+}
--- /dev/null
+#ifndef SRC_SIGNED_DATA_H_
+#define SRC_SIGNED_DATA_H_
+
+/* Some wrappers for libcmscodec's SignedData. */
+
+#include <libcmscodec/SignedData.h>
+
+int signed_data_decode(ANY_t *coded, struct SignedData **result);
+void signed_data_free(struct SignedData *sdata);
+
+#endif /* SRC_SIGNED_DATA_H_ */
#ifndef TAL_H_
#define TAL_H_
+/* This is RFC 7730. */
+
struct tal;
int tal_load(const char *, struct tal **);