From: Job Snijders Subject: rpki-client: in filemode read concatenated sequence of objects To: tech@openbsd.org Date: Sun, 12 Jul 2026 12:34:04 +0000 RPKI objects are self-delimited. This changeset adds in filemode the ability to read a concatenated sequence of DER-encoded objects. This is particularly useful when debugging erik protocol data where concatenated sequences are used: https://datatracker.ietf.org/doc/html/draft-ietf-sidrops-rpki-erik-protocol-05#section-6.1 Example: $ find /var/cache/rpki-client/ta -type f | xargs cat > /tmp/stream $ rpki-client -f /tmp/stream | egrep -v "^File:" | sha256 fba67a4329c65c8bc2473dd60890ece977cbc132a2a882351c2e2cac5d49e978 $ find /var/cache/rpki-client/ta -type f | xargs rpki-client -f | egrep -v "^File:" | sha256 fba67a4329c65c8bc2473dd60890ece977cbc132a2a882351c2e2cac5d49e978 There is an XXX to also add support to be able to read concatenated gzip streams. Later on I'd also like to add the ability to read from stdin ('rpki-client -f -'). Man page will also need a slight tweak. Before wandering off too far, I thought I should check in with the group. Feedback? Index: filemode.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/filemode.c,v diff -u -p -r1.86 filemode.c --- filemode.c 9 Jul 2026 11:39:19 -0000 1.86 +++ filemode.c 12 Jul 2026 11:57:44 -0000 @@ -456,28 +456,6 @@ proc_parser_file(char *file, unsigned ch } } - if (rtype_from_file_extension(file) == RTYPE_GZ) { - unsigned char *full_buf = NULL; - size_t full_len; - char *gz_ext; - - if ((full_buf = inflate_buffer(buf, len, &full_len)) == NULL) { - warnx("%s: gzip decompression failed", file); - goto out; - } - if (buf != in_buf) - free(buf); - buf = full_buf; - len = full_len; - - /* zap trailing .gz */ - if ((gz_ext = strrchr(file, '.')) == NULL) { - warnx("%s: unreachable: missing . in filename?", file); - goto out; - } - *gz_ext = '\0'; - } - if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) errx(1, "EVP_Digest failed in %s", __func__); Index: main.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/main.c,v diff -u -p -r1.311 main.c --- main.c 8 Jul 2026 17:49:04 -0000 1.311 +++ main.c 12 Jul 2026 11:57:45 -0000 @@ -45,6 +45,8 @@ #include +#include + #include "extern.h" #include "version.h" @@ -480,21 +482,84 @@ queue_add_from_mft(const struct mft *mft static void queue_add_file(const char *file, enum rtype type, int talid) { - unsigned char *buf = NULL; - char *nfile; - size_t len = 0; + unsigned char *buf = NULL, *obj_buf; + const unsigned char *cbuf, *objp; + char *nfile; + ASN1_SEQUENCE_ANY *as; + size_t len = 0, buf_len, obj_len; if (!filemode || strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) { buf = load_file(file, &len); if (buf == NULL) err(1, "%s", file); } + + if (type == RTYPE_TAL || rtype_from_file_extension(file) == RTYPE_TAL) { + if ((nfile = strdup(file)) == NULL) + err(1, NULL); + entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, + talid, 0, NULL); + return; + } - if ((nfile = strdup(file)) == NULL) - err(1, NULL); - /* Not in a repository, so directly add to queue. */ - entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, talid, 0, - NULL); + /* In case of rsync URI, load the object later on from the cachedir. */ + if (filemode && strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { + if ((nfile = strdup(file)) == NULL) + err(1, NULL); + entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, NULL, 0, + talid, 0, NULL); + return; + } + + assert(filemode); + + /* XXX: only inflates the first gzip member contained in buf */ + if (rtype_from_file_extension(file) == RTYPE_GZ) { + unsigned char *full_buf = NULL; + size_t full_len; + char *gz_ext; + + if ((full_buf = inflate_buffer(buf, len, &full_len)) == NULL) { + warnx("%s: gzip decompression failed", file); + free(buf); + return; + } + free(buf); + buf = full_buf; + len = full_len; + + /* zap trailing .gz */ + if ((gz_ext = strrchr(file, '.')) == NULL) + errx(1, "%s: unreachable: no . in filename?", file); + *gz_ext = '\0'; + } + + cbuf = buf; + buf_len = len; + do { + objp = cbuf; + if ((as = d2i_ASN1_SEQUENCE_ANY(NULL, &cbuf, len)) == NULL) { + warnx("%s: malformed DER object", file); + goto out; + } + ASN1_item_free((ASN1_VALUE *)as, &ASN1_SEQUENCE_ANY_it); + as = NULL; + + obj_len = cbuf - objp; + len = len - obj_len; + + if ((obj_buf = calloc(1, obj_len)) == NULL) + err(1, NULL); + memcpy(obj_buf, objp, obj_len); + + if ((nfile = strdup(file)) == NULL) + err(1, NULL); + entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, obj_buf, + obj_len, 0, 0, NULL); + } while (cbuf != buf + buf_len); + + out: + free(buf); } /*