]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Improve the file type detector for --mode=print
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Mon, 29 Apr 2024 21:52:45 +0000 (15:52 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 30 Apr 2024 21:17:17 +0000 (15:17 -0600)
Guesses the file type from the ASN1 shape.

Progress for #122.

src/asn1/asn1c/ANY.c
src/asn1/asn1c/CRL.c
src/asn1/asn1c/CRL.h
src/asn1/asn1c/Certificate.c
src/asn1/asn1c/Certificate.h
src/asn1/asn1c/CertificateSet.c
src/asn1/asn1c/OBJECT_IDENTIFIER.c
src/print_file.c

index 75240df47c79a8bdf10b78b0e84713709af425f2..a478f9df53750f61e7b0ee76510fd228c8c30898 100644 (file)
@@ -121,6 +121,7 @@ ANY_new_fromType(asn_TYPE_descriptor_t *td, void *sptr) {
        }
 }
 
+/* FIXME use this more. */
 int
 ANY_to_type(ANY_t *st, asn_TYPE_descriptor_t *td, void **struct_ptr) {
        asn_dec_rval_t rval;
index 5c3b240802ed4abbd042d4ef446bec8f28bb685b..f84ac734c641d46ecd5a23c14c8dd8ec7de74add 100644 (file)
@@ -108,13 +108,12 @@ fail:     json_decref(root);
 }
 
 json_t *
-CRL_encode_json(ANY_t *ber)
+CRL_file2json(FILE *file)
 {
-       const unsigned char *tmp = (const unsigned char *) ber->buf;
        X509_CRL *crl;
        json_t *root;
 
-       crl = d2i_X509_CRL(NULL, &tmp, ber->size);
+       crl = d2i_X509_CRL_fp(file, NULL);
        if (crl == NULL)
                return NULL;
 
index 3c09eb87d9bd64803141b3341952a1cbb4005dae..c49c8b8c1407bc5717a9096fed5d0ef8f3c538bd 100644 (file)
@@ -1,9 +1,9 @@
 #ifndef SRC_ASN1_ASN1C_CRL_H_
 #define SRC_ASN1_ASN1C_CRL_H_
 
+#include <stdio.h>
 #include <jansson.h>
-#include "asn1/asn1c/ANY.h"
 
-json_t *CRL_encode_json(ANY_t *ber);
+json_t *CRL_file2json(FILE *);
 
 #endif /* SRC_ASN1_ASN1C_CRL_H_ */
index 5bfcfdfdfa924f87e51d1045b73be2c63ad4e86f..0defcac8e80285507d93b799536a1794117b6800 100644 (file)
@@ -165,7 +165,7 @@ fail:       json_decref(root);
 }
 
 json_t *
-Certificate_encode_json(ANY_t *ber)
+Certificate_any2json(ANY_t *ber)
 {
        const unsigned char *tmp;
        X509 *cert;
@@ -188,3 +188,19 @@ Certificate_encode_json(ANY_t *ber)
        X509_free(cert);
        return root;
 }
+
+json_t *
+Certificate_file2json(FILE *file)
+{
+       X509 *cert;
+       json_t *root;
+
+       cert = d2i_X509_fp(file, NULL);
+       if (cert == NULL)
+               return NULL;
+
+       root = x509_to_json(cert);
+
+       X509_free(cert);
+       return root;
+}
index d2dccf459547ce71e1922875307c8b1955604300..6400f31a1afd0dda0019b99a9dc2cc86b750f4e3 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef SRC_ASN1_ASN1C_CERTIFICATE_H_
 #define SRC_ASN1_ASN1C_CERTIFICATE_H_
 
+#include <stdio.h>
 #include <jansson.h>
 #include "asn1/asn1c/ANY.h"
 
-json_t *Certificate_encode_json(ANY_t *ber);
+json_t *Certificate_any2json(ANY_t *);
+json_t *Certificate_file2json(FILE *);
 
 #endif /* SRC_ASN1_ASN1C_CERTIFICATE_H_ */
index dcdc88ba72a43616d087b31e8aa054d63241e88f..012ef3b04026547757520579487f0afec1e7a1ce 100644 (file)
@@ -26,7 +26,7 @@ CertificateSet_encode_json(const struct asn_TYPE_descriptor_s *td,
        list = _A_CSET_FROM_VOID(sptr);
 
        for (i = 0; i < list->count; i++) {
-               json_t *node = Certificate_encode_json(list->array[i]);
+               json_t *node = Certificate_any2json(list->array[i]);
                if (node == NULL)
                        goto fail;
                if (json_array_append_new(result, node) < 0)
index 04094ac439fddac52a2c782b1777a4f780b33937..e330ae82512fc58d1109de9d5e3ddc42ffc64ee8 100644 (file)
@@ -595,6 +595,7 @@ is_raw(OBJECT_IDENTIFIER_t const *oid, unsigned char const *raw, size_t size)
        return (oid->size == size) && memcmp(oid->buf, raw, size) == 0;
 }
 
+/* FIXME ber_decode_primitive() */
 bool
 OBJECT_IDENTIFIER_is_rsaEncryption(OBJECT_IDENTIFIER_t const *oid)
 {
index 830156f459ddc495c57c17dc29530bb4d2738d7c..2e08a1caabf0e1145c014f5b8274298c44c3c2ae 100644 (file)
 #include "asn1/asn1c/Certificate.h"
 #include "asn1/asn1c/CRL.h"
 
-static json_t *
-libcrypto2json(char const *filename, json_t *(*encoder)(ANY_t *))
+enum file_type {
+       FT_UNKNOWN,
+       FT_SIGNED_OBJECT,
+       FT_CERTIFICATE,
+       FT_CRL,
+};
+
+#define HDRSIZE 32
+
+static unsigned char *
+skip_sequence(unsigned char *buf, unsigned char *cursor)
 {
-       struct file_contents fc;
-       ANY_t any;
-       json_t *json;
-       int error;
+       ber_tlv_len_t len;
+       ssize_t len_len;
 
-       error = file_load(filename, &fc);
-       if (error)
+       len_len = ber_fetch_length(1, cursor, HDRSIZE - (cursor - buf), &len);
+       if (len_len <= 0)
                return NULL;
+       cursor += len_len;
+       return (cursor <= (buf + HDRSIZE)) ? cursor : NULL;
+}
 
-       memset(&any, 0, sizeof(any));
-       any.buf = fc.buffer;
-       any.size = fc.buffer_size;
+static unsigned char *
+skip_integer(unsigned char *buf, unsigned char *cursor)
+{
+       ber_tlv_len_t len;
+       ssize_t len_len;
 
-       json = encoder(&any);
+       len_len = ber_fetch_length(0, cursor, HDRSIZE - (cursor - buf), &len);
+       if (len_len <= 0) {
+               pr_op_debug("aoe");
+               return NULL;
+       }
+       cursor += len_len + len;
+       return (cursor <= (buf + HDRSIZE)) ? cursor : NULL;
+}
 
-       file_free(&fc);
-       return json;
+static int
+guess_file_type(FILE *file)
+{
+       unsigned char buf[HDRSIZE];
+       unsigned char *ptr;
+
+       if (fread(buf, 1, HDRSIZE, file) != HDRSIZE) {
+               pr_op_debug("File is too small or generic IO error.");
+               return FT_UNKNOWN;
+       }
+       rewind(file);
+
+       if (buf[0] != 0x30) {
+               pr_op_debug("File doesn't start with a SEQUENCE.");
+               return FT_UNKNOWN;
+       }
+       ptr = skip_sequence(buf, buf + 1);
+       if (ptr == NULL) {
+               pr_op_debug("Cannot skip first sequence length.");
+               return FT_UNKNOWN;
+       }
+
+       if (*ptr == 0x06) {
+               pr_op_debug("SEQ containing OID.");
+               return FT_SIGNED_OBJECT;
+       }
+       if (*ptr != 0x30) {
+               pr_op_debug("SEQ containing unexpected: 0x%x", *ptr);
+               return FT_UNKNOWN;
+       }
+
+       ptr = skip_sequence(buf, ptr + 1);
+       if (ptr == NULL) {
+               pr_op_debug("Cannot skip second sequence length.");
+               return FT_UNKNOWN;
+       }
+       ptr = skip_integer(buf, ptr + 1);
+       if (ptr == NULL) {
+               pr_op_debug("Cannot skip version number.");
+               return FT_UNKNOWN;
+       }
+
+       if (*ptr == 0x02) {
+               pr_op_debug("SEQ containing SEQ containing (INT, INT).");
+               return FT_CERTIFICATE;
+       }
+       if (*ptr == 0x30) {
+               pr_op_debug("SEQ containing SEQ containing (INT, SEQ).");
+               return FT_CRL;
+       }
+
+       pr_op_debug("SEQ containing SEQ containing unexpected: 0x%x", *ptr);
+       return FT_UNKNOWN;
 }
 
-int
-print_file(void)
+static struct ContentInfo *
+file2ci(FILE *file)
+{
+#define BUFFER_SIZE 1024
+       struct ContentInfo *ci = NULL;
+       unsigned char buffer[BUFFER_SIZE];
+       size_t consumed;
+       bool eof;
+       asn_dec_rval_t res;
+
+       eof = false;
+       do {
+               consumed = fread(buffer, 1, BUFFER_SIZE, file);
+               if (consumed < BUFFER_SIZE) {
+                       if (feof(file)) {
+                               eof = true;
+                       } else if (ferror(file)) {
+                               pr_op_err("ferror.");
+                               return NULL;
+                       } else {
+                               pr_op_err("?");
+                               return NULL;
+                       }
+               }
+
+               res = ber_decode(NULL, &asn_DEF_ContentInfo, (void **)&ci, buffer, consumed);
+               pr_op_debug("Consumed: %zu", res.consumed);
+
+               switch (res.code) {
+               case RC_OK:
+                       if (!eof)
+                               pr_op_warn("File has trailing bytes.");
+                       return ci;
+
+               case RC_WMORE:
+                       if (eof) {
+                               pr_op_err("File ended prematurely.");
+                               return NULL;
+                       }
+                       break;
+
+               case RC_FAIL:
+                       pr_op_err("Unsuccessful parse.");
+                       return NULL;
+               }
+       } while (true);
+}
+
+static json_t *
+asn1c2json(FILE *file)
 {
-       char const *filename;
        struct ContentInfo *ci;
        json_t *json;
-       int error;
 
-       filename = config_get_payload();
-       if (str_ends_with(filename, ".cer")) {
-               json = libcrypto2json(filename, Certificate_encode_json);
-       } else if (str_ends_with(filename, ".crl")) {
-               json = libcrypto2json(filename, CRL_encode_json);
+       ci = file2ci(file);
+       if (ci == NULL)
+               return NULL;
 
-       } else {
-               error = content_info_load(filename, &ci);
-               if (error)
-                       return error;
+       json = json_encode(&asn_DEF_ContentInfo, ci);
+
+       ASN_STRUCT_FREE(asn_DEF_ContentInfo, ci);
+       return json;
+}
 
-               json = json_encode(&asn_DEF_ContentInfo, ci);
+int
+print_file(void)
+{
+       char const *filename = config_get_payload();
+       FILE *file;
+       json_t *json = NULL;
+       int error = 0;
 
-               ASN_STRUCT_FREE(asn_DEF_ContentInfo, ci);
+       file = fopen(filename, "rb");
+       if (file == NULL)
+               return pr_op_err("Cannot open file: %s", strerror(errno));
+
+       switch (guess_file_type(file)) {
+       case FT_UNKNOWN:
+               error = pr_op_err("Unrecognized file type.");
+               break;
+       case FT_SIGNED_OBJECT:
+               json = asn1c2json(file);
+               break;
+       case FT_CERTIFICATE:
+               json = Certificate_file2json(file);
+               break;
+       case FT_CRL:
+               json = CRL_file2json(file);
+               break;
        }
 
-       if (json == NULL) {
-               pr_op_err("Error parsing object.");
+       fclose(file);
+       if (error)
                return error;
-       }
+       if (json == NULL)
+               return pr_op_err("Unable to parse.");
 
        errno = 0;
        if (json_dumpf(json, stdout, JSON_INDENT(4)) < 0) {
@@ -67,9 +205,11 @@ print_file(void)
                        pr_op_err("Error writing JSON to file: %s", strerror(error));
                else
                        pr_op_err("Unknown error writing JSON to file.");
+               goto end;
        }
 
-       json_decref(json);
+       error = 0;
        printf("\n");
+end:   json_decref(json);
        return error;
 }