Index | Thread | Search

From:
Theo Buehler <tb@theobuehler.org>
Subject:
rpki-client: more smarts to determine the file type
To:
tech@openbsd.org
Date:
Fri, 1 Aug 2025 17:08:36 +0200

Download raw body.

Thread
  • Theo Buehler:

    rpki-client: more smarts to determine the file type

For debugging the Erik protocol WIP, Job needs to be able to point
rpki-client in filemode at files without file extensions, so we need to
have a fallback. This attempts to parse the file contents as a DER blob
first as a CMS content, for which we can perform some checks to figure
out the signedData type (CMS_get0_SignerInfos() only returns non-NULL if
cms info has type signedData, then we can retrieve the eContentType and
compare it against the supported OIDs.

Then try certs and CRLs. We could add more smarts, but this should be
good enough.

This diff depends on the extern churn I just committed.

Index: filemode.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/filemode.c,v
diff -u -p -r1.66 filemode.c
--- filemode.c	20 Jul 2025 12:00:49 -0000	1.66
+++ filemode.c	1 Aug 2025 14:58:14 -0000
@@ -32,6 +32,7 @@
 #include <imsg.h>
 
 #include <openssl/asn1.h>
+#include <openssl/cms.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
@@ -330,6 +331,80 @@ print_signature_path(const char *crl, co
 }
 
 /*
+ * Attempt to determine the file type from the DER by trial and error.
+ */
+static enum rtype
+rtype_from_der(const char *fn, const unsigned char *der, size_t len)
+{
+	CMS_ContentInfo		*cms = NULL;
+	X509			*x509 = NULL;
+	X509_CRL		*crl = NULL;
+	const unsigned char	*p;
+	enum rtype		 rtype = RTYPE_INVALID;
+
+	/* Does der parse as a CMS signed object? */
+	p = der;
+	if ((cms = d2i_CMS_ContentInfo(NULL, &p, len)) != NULL) {
+		const ASN1_OBJECT *obj;
+
+		if (CMS_get0_SignerInfos(cms) == NULL) {
+			warnx("%s: CMS object not signedData", fn);
+			goto out;
+		}
+
+		if ((obj = CMS_get0_eContentType(cms)) == NULL) {
+			warnx("%s: RFC 6488, section 2.1.3.1: eContentType: "
+			    "OID object is NULL", fn);
+			goto out;
+		}
+
+		if (OBJ_cmp(obj, aspa_oid) == 0)
+			rtype = RTYPE_ASPA;
+		else if (OBJ_cmp(obj, mft_oid) == 0)
+			rtype = RTYPE_MFT;
+		else if (OBJ_cmp(obj, gbr_oid) == 0)
+			rtype = RTYPE_GBR;
+		else if (OBJ_cmp(obj, roa_oid) == 0)
+			rtype = RTYPE_ROA;
+		else if (OBJ_cmp(obj, rsc_oid) == 0)
+			rtype = RTYPE_RSC;
+		else if (OBJ_cmp(obj, spl_oid) == 0)
+			rtype = RTYPE_SPL;
+		else if (OBJ_cmp(obj, tak_oid) == 0)
+			rtype = RTYPE_TAK;
+
+		goto out;
+	}
+
+	/* Does der parse as a certificate? */
+	p = der;
+	if ((x509 = d2i_X509(NULL, &p, len)) != NULL) {
+		rtype = RTYPE_CER;
+		goto out;
+	}
+
+	/* Does der parse as a CRL? */
+	p = der;
+	if ((crl = d2i_X509_CRL(NULL, &p, len)) != NULL) {
+		rtype = RTYPE_CRL;
+		goto out;
+	}
+
+	/*
+	 * We could add some heuristics for recognizing TALs and geofeed by
+	 * looking for things like "rsync://" and "MII" or "RPKI Signature"
+	 * using memmem(3).
+	 */
+
+ out:
+	CMS_ContentInfo_free(cms);
+	X509_free(x509);
+	X509_CRL_free(crl);
+
+	return rtype;
+}
+
+/*
  * Parse file passed with -f option.
  */
 static void
@@ -394,6 +469,8 @@ proc_parser_file(char *file, unsigned ch
 	free(hash);
 
 	type = rtype_from_file_extension(file);
+	if (type == RTYPE_INVALID)
+		type = rtype_from_der(file, buf, len);
 
 	switch (type) {
 	case RTYPE_ASPA: