From: Theo Buehler Subject: rpki-client: more smarts to determine the file type To: tech@openbsd.org Date: Fri, 1 Aug 2025 17:08:36 +0200 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 #include +#include #include #include #include @@ -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: