diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2021-04-01 16:04:49 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2021-04-01 16:04:49 +0000 |
commit | decf126e1b2a932dbabf54e534aef16df1fdfe2f (patch) | |
tree | 0f4b94d45bd2485c08ea0525382a5b89d363af63 /usr.sbin/rpki-client/rrdp_delta.c | |
parent | 58a55f4dd0baa2c277509867e44d742d7e7e91c5 (diff) |
Initial commit of RRDP (The RPKI Repository Delta Protocol - RFC8182) support
in rpki-client. For now it is off by default.
All XML processing is done in its own process with minimal pledge rights.
It uses the already present https process to fetch the xml files and uses
the master porcess to handle the file IO into the repositories.
RRDP data is stored in the cache under ./rrdp/ and the first directory
is the SHA256 hash of the notify URI.
Fetching snapshots and deltas works to bring the cache up to date.
If something goes wrong rpki-client will fall back to rsync.
RRDP was implemented by Nils Fisher and integrated into rpki-client by myself.
"Time to get it in" deraadt@
Diffstat (limited to 'usr.sbin/rpki-client/rrdp_delta.c')
-rw-r--r-- | usr.sbin/rpki-client/rrdp_delta.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/usr.sbin/rpki-client/rrdp_delta.c b/usr.sbin/rpki-client/rrdp_delta.c new file mode 100644 index 00000000000..7957a72be72 --- /dev/null +++ b/usr.sbin/rpki-client/rrdp_delta.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com> + * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <err.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#include <expat.h> +#include <openssl/sha.h> + +#include "extern.h" +#include "rrdp.h" + +enum delta_scope { + DELTA_SCOPE_NONE, + DELTA_SCOPE_DELTA, + DELTA_SCOPE_PUBLISH, + DELTA_SCOPE_END +}; + +struct delta_xml { + XML_Parser parser; + struct rrdp_session *current; + struct rrdp *rrdp; + struct publish_xml *pxml; + char *session_id; + long long serial; + int version; + enum delta_scope scope; +}; + +enum validate_return { + VALIDATE_RETURN_NO_FILE, + VALIDATE_RETURN_FILE_DEL, + VALIDATE_RETURN_HASH_MISMATCH, + VALIDATE_RETURN_HASH_MATCH +}; + +static void +start_delta_elem(struct delta_xml *dxml, const char **attr) +{ + XML_Parser p = dxml->parser; + int has_xmlns = 0; + int i; + + if (dxml->scope != DELTA_SCOPE_NONE) + PARSE_FAIL(p, + "parse failed - entered delta elem unexpectedely"); + for (i = 0; attr[i]; i += 2) { + const char *errstr; + if (strcmp("xmlns", attr[i]) == 0) { + has_xmlns = 1; + continue; + } + if (strcmp("version", attr[i]) == 0) { + dxml->version = strtonum(attr[i + 1], + 1, MAX_VERSION, &errstr); + if (errstr == NULL) + continue; + } + if (strcmp("session_id", attr[i]) == 0) { + dxml->session_id = xstrdup(attr[i + 1]); + continue; + } + if (strcmp("serial", attr[i]) == 0) { + dxml->serial = strtonum(attr[i + 1], + 1, LLONG_MAX, &errstr); + if (errstr == NULL) + continue; + } + PARSE_FAIL(p, "parse failed - non conforming " + "attribute found in delta elem"); + } + if (!(has_xmlns && dxml->version && dxml->session_id && dxml->serial)) + PARSE_FAIL(p, "parse failed - incomplete delta attributes"); + if (strcmp(dxml->current->session_id, dxml->session_id) != 0) + PARSE_FAIL(p, "parse failed - session_id mismatch"); + if (dxml->current->serial != dxml->serial) + PARSE_FAIL(p, "parse failed - serial mismatch"); + + dxml->scope = DELTA_SCOPE_DELTA; +} + +static void +end_delta_elem(struct delta_xml *dxml) +{ + XML_Parser p = dxml->parser; + + if (dxml->scope != DELTA_SCOPE_DELTA) + PARSE_FAIL(p, "parse failed - exited delta " + "elem unexpectedely"); + dxml->scope = DELTA_SCOPE_END; + +} + +static void +start_publish_withdraw_elem(struct delta_xml *dxml, const char **attr, + int withdraw) +{ + XML_Parser p = dxml->parser; + char *uri, hash[SHA256_DIGEST_LENGTH]; + int i, hasUri = 0, hasHash = 0; + enum publish_type pub = PUB_UPD; + + if (dxml->scope != DELTA_SCOPE_DELTA) + PARSE_FAIL(p, "parse failed - entered publish/withdraw " + "elem unexpectedely"); + for (i = 0; attr[i]; i += 2) { + if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) { + if (valid_uri(attr[i + 1], strlen(attr[i + 1]), + "rsync://")) { + uri = xstrdup(attr[i + 1]); + continue; + } + } + if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) { + if (hex_decode(attr[i + 1], hash, sizeof(hash)) == 0) + continue; + } + PARSE_FAIL(p, "parse failed - non conforming " + "attribute found in publish/withdraw elem"); + } + if (hasUri != 1) + PARSE_FAIL(p, + "parse failed - incomplete publish/withdraw attributes"); + if (withdraw && hasHash != 1) + PARSE_FAIL(p, "parse failed - incomplete withdraw attributes"); + + if (withdraw) + pub = PUB_DEL; + else if (hasHash == 0) + pub = PUB_ADD; + dxml->pxml = new_publish_xml(pub, uri, hash, + hasHash ? sizeof(hash) : 0); + dxml->scope = DELTA_SCOPE_PUBLISH; +} + +static void +end_publish_withdraw_elem(struct delta_xml *dxml, int withdraw) +{ + XML_Parser p = dxml->parser; + + if (dxml->scope != DELTA_SCOPE_PUBLISH) + PARSE_FAIL(p, "parse failed - exited publish/withdraw " + "elem unexpectedely"); + + if (publish_done(dxml->rrdp, dxml->pxml) != 0) + PARSE_FAIL(p, "parse failed - bad publish/withdraw elem"); + dxml->pxml = NULL; + + dxml->scope = DELTA_SCOPE_DELTA; +} + +static void +delta_xml_elem_start(void *data, const char *el, const char **attr) +{ + struct delta_xml *dxml = data; + XML_Parser p = dxml->parser; + + /* + * Can only enter here once as we should have no ways to get back to + * NONE scope + */ + if (strcmp("delta", el) == 0) + start_delta_elem(dxml, attr); + /* + * Will enter here multiple times, BUT never nested. will start + * collecting character data in that handler + * mem is cleared in end block, (TODO or on parse failure) + */ + else if (strcmp("publish", el) == 0) + start_publish_withdraw_elem(dxml, attr, 0); + else if (strcmp("withdraw", el) == 0) + start_publish_withdraw_elem(dxml, attr, 1); + else + PARSE_FAIL(p, "parse failed - unexpected elem exit found"); +} + +static void +delta_xml_elem_end(void *data, const char *el) +{ + struct delta_xml *dxml = data; + XML_Parser p = dxml->parser; + + if (strcmp("delta", el) == 0) + end_delta_elem(dxml); + /* + * TODO does this allow <publish></withdraw> or is that caught by basic + * xml parsing + */ + else if (strcmp("publish", el) == 0) + end_publish_withdraw_elem(dxml, 0); + else if (strcmp("withdraw", el) == 0) + end_publish_withdraw_elem(dxml, 1); + else + PARSE_FAIL(p, "parse failed - unexpected elem exit found"); +} + +static void +delta_content_handler(void *data, const char *content, int length) +{ + struct delta_xml *dxml = data; + + if (dxml->scope == DELTA_SCOPE_PUBLISH) + publish_add_content(dxml->pxml, content, length); +} + +struct delta_xml * +new_delta_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r) +{ + struct delta_xml *dxml; + + if ((dxml = calloc(1, sizeof(*dxml))) == NULL) + err(1, "%s", __func__); + dxml->parser = p; + dxml->current = rs; + dxml->rrdp = r; + + if (XML_ParserReset(dxml->parser, "US-ASCII") != XML_TRUE) + errx(1, "%s: XML_ParserReset failed", __func__); + + XML_SetElementHandler(dxml->parser, delta_xml_elem_start, + delta_xml_elem_end); + XML_SetCharacterDataHandler(dxml->parser, delta_content_handler); + XML_SetUserData(dxml->parser, dxml); + + return dxml; +} + +void +free_delta_xml(struct delta_xml *dxml) +{ + if (dxml == NULL) + return; + + free(dxml->session_id); + free_publish_xml(dxml->pxml); + free(dxml); +} + +void +log_delta_xml(struct delta_xml *dxml) +{ + logx("version: %d", dxml->version); + logx("session_id: %s serial: %lld", dxml->session_id, dxml->serial); +} |