Instead of adding similar checks all over the place introduce a valid_uri() function that checks if a URI is valid enough for rpki-client. rpki-client does not accept files or directories starting with ., bails on URI that have strange characters and valid_uri() will also check that the protocol is the expected one if provided.
This diff converts the 4 places where URI are handled. -- :wq Claudio Index: cert.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v retrieving revision 1.27 diff -u -p -r1.27 cert.c --- cert.c 18 Feb 2021 16:23:17 -0000 1.27 +++ cert.c 5 Mar 2021 12:33:50 -0000 @@ -19,7 +19,6 @@ #include <arpa/inet.h> #include <assert.h> -#include <ctype.h> #include <err.h> #include <inttypes.h> #include <stdarg.h> @@ -140,34 +139,22 @@ sbgp_addr(struct parse *p, * Returns zero on failure, non-zero on success. */ static int -sbgp_sia_resource_notify(struct parse *p, - const unsigned char *d, size_t dsz) +sbgp_sia_resource_notify(struct parse *p, const char *d, size_t dsz) { - size_t i; - if (p->res->notify != NULL) { warnx("%s: RFC 6487 section 4.8.8: SIA: " "Notify location already specified", p->fn); return 0; } + if ((p->res->notify = strndup(d, dsz)) == NULL) + err(1, NULL); + /* Make sure it's a https:// address. */ - if (dsz <= 8 || strncasecmp(d, "https://", 8)) { - warnx("%s: RFC 8182 section 3.2: not using https schema", - p->fn); + if (!valid_uri(p->res->notify, "https://")) { + warnx("%s: RFC 8182 section 3.2: bad Notify URI", p->fn); return 0; } - /* make sure only US-ASCII chars are in the URL */ - for (i = 0; i < dsz; i++) { - if (isalnum(d[i]) || ispunct(d[i])) - continue; - warnx("%s: invalid URI", p->fn); - return 0; - } - - - if ((p->res->notify = strndup((const char *)d, dsz)) == NULL) - err(1, NULL); return 1; } @@ -177,40 +164,29 @@ sbgp_sia_resource_notify(struct parse *p * Returns zero on failure, non-zero on success. */ static int -sbgp_sia_resource_mft(struct parse *p, - const unsigned char *d, size_t dsz) +sbgp_sia_resource_mft(struct parse *p, const char *d, size_t dsz) { - size_t i; - if (p->res->mft != NULL) { warnx("%s: RFC 6487 section 4.8.8: SIA: " "MFT location already specified", p->fn); return 0; } - /* Make sure it's an MFT rsync address. */ - if (dsz <= 8 || strncasecmp(d, "rsync://", 8)) { - warnx("%s: RFC 6487 section 4.8.8: not using rsync schema", - p->fn); - return 0; - } - if (strcasecmp(d + dsz - 4, ".mft") != 0) { warnx("%s: RFC 6487 section 4.8.8: SIA: " - "invalid rsync URI suffix", p->fn); - return 0; - } - /* make sure only US-ASCII chars are in the URL */ - for (i = 0; i < dsz; i++) { - if (isalnum(d[i]) || ispunct(d[i])) - continue; - warnx("%s: invalid URI", p->fn); + "not an MFT file", p->fn); return 0; } - if ((p->res->mft = strndup((const char *)d, dsz)) == NULL) + if ((p->res->mft = strndup(d, dsz)) == NULL) err(1, NULL); + /* Make sure it's an MFT rsync address. */ + if (!valid_uri(p->res->mft, "rsync://")) { + warnx("%s: RFC 6487 section 4.8.8: bad MFT location", p->fn); + return 0; + } + return 1; } @@ -219,35 +195,24 @@ sbgp_sia_resource_mft(struct parse *p, * Returns zero on failure, non-zero on success. */ static int -sbgp_sia_resource_carepo(struct parse *p, - const unsigned char *d, size_t dsz) +sbgp_sia_resource_carepo(struct parse *p, const char *d, size_t dsz) { - size_t i; - if (p->res->repo != NULL) { warnx("%s: RFC 6487 section 4.8.8: SIA: " "CA repository already specified", p->fn); return 0; } + if ((p->res->repo = strndup(d, dsz)) == NULL) + err(1, NULL); + /* Make sure it's an rsync:// address. */ - if (dsz <= 8 || strncasecmp(d, "rsync://", 8)) { - warnx("%s: RFC 6487 section 4.8.8: not using rsync schema", + if (!valid_uri(p->res->repo, "rsync://")) { + warnx("%s: RFC 6487 section 4.8.8: bad CA repository URI", p->fn); return 0; } - /* make sure only US-ASCII chars are in the URL */ - for (i = 0; i < dsz; i++) { - if (isalnum(d[i]) || ispunct(d[i])) - continue; - warnx("%s: invalid URI", p->fn); - return 0; - } - - if ((p->res->repo = strndup((const char *)d, dsz)) == NULL) - err(1, NULL); - return 1; } @@ -1175,7 +1140,6 @@ out: struct cert * cert_parse(X509 **xp, const char *fn) { - return cert_parse_inner(xp, fn, 0); } Index: extern.h =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v retrieving revision 1.50 diff -u -p -r1.50 extern.h --- extern.h 4 Mar 2021 13:01:41 -0000 1.50 +++ extern.h 5 Mar 2021 12:36:51 -0000 @@ -354,6 +354,7 @@ int valid_ta(const char *, struct auth int valid_cert(const char *, struct auth_tree *, const struct cert *); int valid_roa(const char *, struct auth_tree *, struct roa *); +int valid_uri(const char *, const char *); /* Working with CMS files. */ Index: tal.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/tal.c,v retrieving revision 1.27 diff -u -p -r1.27 tal.c --- tal.c 19 Feb 2021 10:23:50 -0000 1.27 +++ tal.c 5 Mar 2021 12:34:54 -0000 @@ -82,7 +82,6 @@ tal_parse_buffer(const char *fn, char *b char *nl, *line, *f, *file = NULL; unsigned char *der; size_t sz, dersz; - ssize_t i; int rc = 0; struct tal *tal = NULL; EVP_PKEY *pkey = NULL; @@ -103,9 +102,7 @@ tal_parse_buffer(const char *fn, char *b break; /* make sure only US-ASCII chars are in the URL */ - for (i = 0; i < nl - line; i++) { - if (isalnum(line[i]) || ispunct(line[i])) - continue; + if (!valid_uri(line, NULL)) { warnx("%s: invalid URI", fn); goto out; } Index: validate.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/validate.c,v retrieving revision 1.11 diff -u -p -r1.11 validate.c --- validate.c 12 Sep 2020 15:46:48 -0000 1.11 +++ validate.c 5 Mar 2021 12:36:40 -0000 @@ -19,7 +19,9 @@ #include <arpa/inet.h> #include <assert.h> +#include <ctype.h> #include <err.h> +#include <fcntl.h> #include <inttypes.h> #include <stdarg.h> #include <stdlib.h> @@ -235,6 +237,40 @@ valid_roa(const char *fn, struct auth_tr warnx("%s: RFC 6482: uncovered IP: " "%s", fn, buf); tracewarn(a); + return 0; + } + + return 1; +} + +/* + * Validate a URI to make sure it is pure ASCII and does not point backwards + * or doing some other silly tricks. To enforce the protocol pass either + * https:// or rsync:// as proto, if NULL is passed no protocol is enforced. + * Returns 1 if valid, 0 otherwise. + */ +int +valid_uri(const char *uri, const char *proto) +{ + const unsigned char *u; + + for (u = uri; *u != '\0'; u++) + if (!isalnum(*u) && !ispunct(*u)) { + warnx("URI: non-ascii %s", uri); + return 0; + } + + if (proto) { + size_t plen = strlen(proto); + if (strncasecmp(uri, proto, plen) != 0) { + warnx("URI: not proto %s: %s", proto, uri); + return 0; + } + } + + /* do not allow files or directories to start with a . */ + if (strstr(uri, "/.") != NULL) { + warnx("URI: dot-file %s", uri); return 0; }