From: Alberto Leiva Popper Date: Mon, 29 Apr 2024 21:52:45 +0000 (-0600) Subject: Improve the file type detector for --mode=print X-Git-Tag: 1.6.2~33 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=bbd9cde614240869fb3f8d55a4fc025806942f46;p=thirdparty%2FFORT-validator.git Improve the file type detector for --mode=print Guesses the file type from the ASN1 shape. Progress for #122. --- diff --git a/src/asn1/asn1c/ANY.c b/src/asn1/asn1c/ANY.c index 75240df4..a478f9df 100644 --- a/src/asn1/asn1c/ANY.c +++ b/src/asn1/asn1c/ANY.c @@ -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; diff --git a/src/asn1/asn1c/CRL.c b/src/asn1/asn1c/CRL.c index 5c3b2408..f84ac734 100644 --- a/src/asn1/asn1c/CRL.c +++ b/src/asn1/asn1c/CRL.c @@ -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; diff --git a/src/asn1/asn1c/CRL.h b/src/asn1/asn1c/CRL.h index 3c09eb87..c49c8b8c 100644 --- a/src/asn1/asn1c/CRL.h +++ b/src/asn1/asn1c/CRL.h @@ -1,9 +1,9 @@ #ifndef SRC_ASN1_ASN1C_CRL_H_ #define SRC_ASN1_ASN1C_CRL_H_ +#include #include -#include "asn1/asn1c/ANY.h" -json_t *CRL_encode_json(ANY_t *ber); +json_t *CRL_file2json(FILE *); #endif /* SRC_ASN1_ASN1C_CRL_H_ */ diff --git a/src/asn1/asn1c/Certificate.c b/src/asn1/asn1c/Certificate.c index 5bfcfdfd..0defcac8 100644 --- a/src/asn1/asn1c/Certificate.c +++ b/src/asn1/asn1c/Certificate.c @@ -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; +} diff --git a/src/asn1/asn1c/Certificate.h b/src/asn1/asn1c/Certificate.h index d2dccf45..6400f31a 100644 --- a/src/asn1/asn1c/Certificate.h +++ b/src/asn1/asn1c/Certificate.h @@ -1,9 +1,11 @@ #ifndef SRC_ASN1_ASN1C_CERTIFICATE_H_ #define SRC_ASN1_ASN1C_CERTIFICATE_H_ +#include #include #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_ */ diff --git a/src/asn1/asn1c/CertificateSet.c b/src/asn1/asn1c/CertificateSet.c index dcdc88ba..012ef3b0 100644 --- a/src/asn1/asn1c/CertificateSet.c +++ b/src/asn1/asn1c/CertificateSet.c @@ -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) diff --git a/src/asn1/asn1c/OBJECT_IDENTIFIER.c b/src/asn1/asn1c/OBJECT_IDENTIFIER.c index 04094ac4..e330ae82 100644 --- a/src/asn1/asn1c/OBJECT_IDENTIFIER.c +++ b/src/asn1/asn1c/OBJECT_IDENTIFIER.c @@ -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) { diff --git a/src/print_file.c b/src/print_file.c index 830156f4..2e08a1ca 100644 --- a/src/print_file.c +++ b/src/print_file.c @@ -9,56 +9,194 @@ #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; }