rpki_validator_SOURCES += common.h common.c
rpki_validator_SOURCES += debug.h debug.c
rpki_validator_SOURCES += file.h file.c
+rpki_validator_SOURCES += hash.h hash.c
rpki_validator_SOURCES += line_file.h line_file.c
rpki_validator_SOURCES += log.h log.c
rpki_validator_SOURCES += resource.h resource.c
* error code.
*/
pr_errno(error,
- "File reading error. Error message (apparently)",
- file_name);
+ "File reading error. Error message (apparently)");
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.
+ * As far as I can tell from the man page, fread() cannot return
+ * less bytes than requested like read() does. It's either
+ * "consumed everything", "EOF reached" or error.
*/
pr_err("Likely programming error: fread() < file size");
pr_err("fr:%zu bs:%zu EOF:%d", fread_result, fc->buffer_size,
--- /dev/null
+#include "hash.h"
+
+#include <errno.h>
+#include <openssl/evp.h>
+#include <sys/stat.h>
+
+#include "log.h"
+
+#define ALGORITHM "sha256"
+static EVP_MD const *md;
+
+int
+hash_init(void)
+{
+ md = EVP_get_digestbyname(ALGORITHM);
+ if (md == NULL) {
+ printf("Unknown message digest %s\n", ALGORITHM);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+hash_file(char *filename, unsigned char *result, unsigned int *result_len)
+{
+ FILE *file;
+ struct stat stat;
+ unsigned char *buffer;
+ __blksize_t buffer_len;
+ size_t consumed;
+ EVP_MD_CTX *ctx;
+ int error;
+
+ file = fopen(filename, "rb");
+ if (file == NULL)
+ return pr_errno(errno, "Could not open file '%s'", filename);
+
+ buffer_len = (fstat(fileno(file), &stat) == 0) ? stat.st_blksize : 1024;
+ buffer = malloc(buffer_len);
+ if (buffer == NULL) {
+ pr_err("Out of memory.");
+ error = -ENOMEM;
+ goto end1;
+ }
+
+ ctx = EVP_MD_CTX_new();
+ if (ctx == NULL) {
+ pr_err("Out of memory.");
+ error = -ENOMEM;
+ goto end2;
+ }
+
+ if (!EVP_DigestInit_ex(ctx, md, NULL)) {
+ error = crypto_err("EVP_DigestInit_ex() failed");
+ goto end3;
+ }
+
+ do {
+ consumed = fread(buffer, 1, buffer_len, file);
+ error = ferror(file);
+ if (error) {
+ pr_errno(error,
+ "File reading error. Error message (apparently)");
+ goto end3;
+ }
+
+ if (!EVP_DigestUpdate(ctx, buffer, consumed)) {
+ error = crypto_err("EVP_DigestUpdate() failed");
+ goto end3;
+ }
+
+ } while (!feof(file));
+
+ if (!EVP_DigestFinal_ex(ctx, result, result_len))
+ error = crypto_err("EVP_DigestFinal_ex() failed");
+
+end3:
+ EVP_MD_CTX_free(ctx);
+end2:
+ free(buffer);
+end1:
+ if (fclose(file) == -1)
+ pr_errno(errno, "fclose() failed");
+ return error;
+}
+
+/**
+ * Computes the hash of the file whose name is @filename, and compares it to
+ * @expected (The "expected" hash). Returns 0 if no errors happened and the
+ * hashes match.
+ */
+int
+hash_validate(char *filename, BIT_STRING_t *expected)
+{
+ unsigned char actual[EVP_MAX_MD_SIZE];
+ unsigned int actual_len;
+ int error;
+
+ if (expected->bits_unused != 0) {
+ pr_err("Hash string has unused bits.");
+ return -EINVAL;
+ }
+
+ error = hash_file(filename, actual, &actual_len);
+ if (error)
+ return error;
+
+ if (expected->size != actual_len)
+ goto mismatch;
+ if (memcmp(expected->buf, actual, actual_len) != 0)
+ goto mismatch;
+
+ return 0;
+
+mismatch:
+ pr_err("File does not match its hash.");
+ return -EINVAL;
+}
--- /dev/null
+#ifndef SRC_HASH_H_
+#define SRC_HASH_H_
+
+#include <libcmscodec/BIT_STRING.h>
+
+int hash_init(void);
+int hash_validate(char *file, BIT_STRING_t *hash);
+
+#endif /* SRC_HASH_H_ */
int error;
PR_ERR(args);
+ fprintf(STDERR, ": ");
error = ERR_GET_REASON(ERR_peek_last_error());
if (error) {
#include "common.h"
#include "debug.h"
+#include "hash.h"
#include "log.h"
#include "thread_var.h"
#include "object/certificate.h"
return -EINVAL;
}
+ error = hash_init();
+ if (error)
+ return error;
+
add_rpki_oids();
thvar_init();
fnstack_store();
#include <libcmscodec/GeneralizedTime.h>
#include <libcmscodec/Manifest.h>
+#include "hash.h"
#include "log.h"
-#include "asn1/oid.h"
#include "thread_var.h"
+#include "asn1/oid.h"
#include "object/certificate.h"
#include "object/crl.h"
#include "object/roa.h"
static int
validate_manifest(struct Manifest *manifest)
{
- int error;
bool is_hash;
+ int error;
/* rfc6486#section-4.2.1 */
if (error)
return error;
+ /* rfc6486#section-6.6 (I guess) */
error = is_hash_algorithm(&manifest->fileHashAlg, &is_hash);
if (error)
return error;
- if (!is_hash)
+ if (!is_hash) {
+ pr_err("The hash algorithm is not SHA256.");
return -EINVAL;
+ }
- /* TODO (critical) validate the file hashes */
+ /* The file hashes will be validated during the traversal. */
return 0;
}
static int
foreach_file(struct manifest *mft, char *extension, foreach_cb cb, void *arg)
{
+ struct FileAndHash *fah;
char *uri;
size_t uri_len;
char *luri; /* "Local URI". As in "URI that we can easily reference." */
int error;
for (i = 0; i < mft->obj->fileList.list.count; i++) {
+ fah = mft->obj->fileList.list.array[i];
+
/*
* IA5String is just a subset of ASCII, so this cast is fine.
* I don't see any guarantees that the string will be
* zero-terminated though, so we'll handle that the hard way.
*/
- uri = (char *) mft->obj->fileList.list.array[i]->file.buf;
- uri_len = mft->obj->fileList.list.array[i]->file.size;
+ uri = (char *) fah->file.buf;
+ uri_len = fah->file.size;
if (file_has_extension(uri, uri_len, extension)) {
error = get_relative_file(mft->file_path, uri, uri_len,
&luri);
if (error)
return error;
+
+ error = hash_validate(luri, &fah->hash);
+ if (error) {
+ free(luri);
+ continue;
+ }
+
error = cb(luri, arg);
+
free(luri);
if (error)
return error;