Skip to content

Commit 2f93e50

Browse files
committed
rpki-client: add more smarts to filemode file type detection
If the file extension doesn't indicate what file type we have, attempt to parse the file's content as DER for CMS content, a cert or a CRL. For CMS we can check if we deal with signedData and inspect its eContentType. h/t jsing ok job
1 parent aaaf52e commit 2f93e50

File tree

1 file changed

+78
-1
lines changed

1 file changed

+78
-1
lines changed

usr.sbin/rpki-client/filemode.c

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $OpenBSD: filemode.c,v 1.66 2025/07/20 12:00:49 tb Exp $ */
1+
/* $OpenBSD: filemode.c,v 1.67 2025/08/01 16:33:58 tb Exp $ */
22
/*
33
* Copyright (c) 2019 Claudio Jeker <[email protected]>
44
* Copyright (c) 2019 Kristaps Dzonsons <[email protected]>
@@ -32,6 +32,7 @@
3232
#include <imsg.h>
3333

3434
#include <openssl/asn1.h>
35+
#include <openssl/cms.h>
3536
#include <openssl/err.h>
3637
#include <openssl/evp.h>
3738
#include <openssl/pem.h>
@@ -329,6 +330,80 @@ print_signature_path(const char *crl, const char *aia, const struct auth *a)
329330
}
330331
}
331332

333+
/*
334+
* Attempt to determine the file type from the DER by trial and error.
335+
*/
336+
static enum rtype
337+
rtype_from_der(const char *fn, const unsigned char *der, size_t len)
338+
{
339+
CMS_ContentInfo *cms = NULL;
340+
X509 *x509 = NULL;
341+
X509_CRL *crl = NULL;
342+
const unsigned char *p;
343+
enum rtype rtype = RTYPE_INVALID;
344+
345+
/* Does der parse as a CMS signed object? */
346+
p = der;
347+
if ((cms = d2i_CMS_ContentInfo(NULL, &p, len)) != NULL) {
348+
const ASN1_OBJECT *obj;
349+
350+
if (CMS_get0_SignerInfos(cms) == NULL) {
351+
warnx("%s: CMS object not signedData", fn);
352+
goto out;
353+
}
354+
355+
if ((obj = CMS_get0_eContentType(cms)) == NULL) {
356+
warnx("%s: RFC 6488, section 2.1.3.1: eContentType: "
357+
"OID object is NULL", fn);
358+
goto out;
359+
}
360+
361+
if (OBJ_cmp(obj, aspa_oid) == 0)
362+
rtype = RTYPE_ASPA;
363+
else if (OBJ_cmp(obj, mft_oid) == 0)
364+
rtype = RTYPE_MFT;
365+
else if (OBJ_cmp(obj, gbr_oid) == 0)
366+
rtype = RTYPE_GBR;
367+
else if (OBJ_cmp(obj, roa_oid) == 0)
368+
rtype = RTYPE_ROA;
369+
else if (OBJ_cmp(obj, rsc_oid) == 0)
370+
rtype = RTYPE_RSC;
371+
else if (OBJ_cmp(obj, spl_oid) == 0)
372+
rtype = RTYPE_SPL;
373+
else if (OBJ_cmp(obj, tak_oid) == 0)
374+
rtype = RTYPE_TAK;
375+
376+
goto out;
377+
}
378+
379+
/* Does der parse as a certificate? */
380+
p = der;
381+
if ((x509 = d2i_X509(NULL, &p, len)) != NULL) {
382+
rtype = RTYPE_CER;
383+
goto out;
384+
}
385+
386+
/* Does der parse as a CRL? */
387+
p = der;
388+
if ((crl = d2i_X509_CRL(NULL, &p, len)) != NULL) {
389+
rtype = RTYPE_CRL;
390+
goto out;
391+
}
392+
393+
/*
394+
* We could add some heuristics for recognizing TALs and geofeed by
395+
* looking for things like "rsync://" and "MII" or "RPKI Signature"
396+
* using memmem(3). If we do this, we should also rename the function.
397+
*/
398+
399+
out:
400+
CMS_ContentInfo_free(cms);
401+
X509_free(x509);
402+
X509_CRL_free(crl);
403+
404+
return rtype;
405+
}
406+
332407
/*
333408
* Parse file passed with -f option.
334409
*/
@@ -394,6 +469,8 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
394469
free(hash);
395470

396471
type = rtype_from_file_extension(file);
472+
if (type == RTYPE_INVALID)
473+
type = rtype_from_der(file, buf, len);
397474

398475
switch (type) {
399476
case RTYPE_ASPA:

0 commit comments

Comments
 (0)