xref: /openbsd-src/usr.sbin/rpki-client/rrdp_snapshot.c (revision 1a5078e916a953408f5c13ac7ebf55fb5c3b0bd9)
1*1a5078e9Sjob /*	$OpenBSD: rrdp_snapshot.c,v 1.10 2024/05/30 09:54:59 job Exp $ */
28ecbadc1Sclaudio /*
38ecbadc1Sclaudio  * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
48ecbadc1Sclaudio  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
58ecbadc1Sclaudio  *
68ecbadc1Sclaudio  * Permission to use, copy, modify, and distribute this software for any
78ecbadc1Sclaudio  * purpose with or without fee is hereby granted, provided that the above
88ecbadc1Sclaudio  * copyright notice and this permission notice appear in all copies.
98ecbadc1Sclaudio  *
108ecbadc1Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118ecbadc1Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128ecbadc1Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138ecbadc1Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148ecbadc1Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158ecbadc1Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168ecbadc1Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178ecbadc1Sclaudio  */
188ecbadc1Sclaudio 
198ecbadc1Sclaudio #include <err.h>
208ecbadc1Sclaudio #include <limits.h>
218ecbadc1Sclaudio #include <stdlib.h>
228ecbadc1Sclaudio #include <string.h>
238ecbadc1Sclaudio #include <stdio.h>
248ecbadc1Sclaudio #include <unistd.h>
258ecbadc1Sclaudio 
268ecbadc1Sclaudio #include <expat.h>
278ecbadc1Sclaudio 
288ecbadc1Sclaudio #include "extern.h"
298ecbadc1Sclaudio #include "rrdp.h"
308ecbadc1Sclaudio 
318ecbadc1Sclaudio enum snapshot_scope {
328ecbadc1Sclaudio 	SNAPSHOT_SCOPE_NONE,
338ecbadc1Sclaudio 	SNAPSHOT_SCOPE_SNAPSHOT,
348ecbadc1Sclaudio 	SNAPSHOT_SCOPE_PUBLISH,
358ecbadc1Sclaudio 	SNAPSHOT_SCOPE_END
368ecbadc1Sclaudio };
378ecbadc1Sclaudio 
388ecbadc1Sclaudio struct snapshot_xml {
398ecbadc1Sclaudio 	XML_Parser		 parser;
408ecbadc1Sclaudio 	struct rrdp_session	*current;
418ecbadc1Sclaudio 	struct rrdp		*rrdp;
428ecbadc1Sclaudio 	struct publish_xml	*pxml;
438ecbadc1Sclaudio 	char			*session_id;
448ecbadc1Sclaudio 	long long		 serial;
458ecbadc1Sclaudio 	int			 version;
468ecbadc1Sclaudio 	enum snapshot_scope	 scope;
478ecbadc1Sclaudio };
488ecbadc1Sclaudio 
498ecbadc1Sclaudio static void
start_snapshot_elem(struct snapshot_xml * sxml,const char ** attr)508ecbadc1Sclaudio start_snapshot_elem(struct snapshot_xml *sxml, const char **attr)
518ecbadc1Sclaudio {
528ecbadc1Sclaudio 	XML_Parser p = sxml->parser;
538ecbadc1Sclaudio 	int has_xmlns = 0;
548ecbadc1Sclaudio 	int i;
558ecbadc1Sclaudio 
568ecbadc1Sclaudio 	if (sxml->scope != SNAPSHOT_SCOPE_NONE)
578ecbadc1Sclaudio 		PARSE_FAIL(p,
588ecbadc1Sclaudio 		    "parse failed - entered snapshot elem unexpectedely");
598ecbadc1Sclaudio 	for (i = 0; attr[i]; i += 2) {
608ecbadc1Sclaudio 		const char *errstr;
618e901cb8Sclaudio 		if (strcmp("xmlns", attr[i]) == 0 &&
628e901cb8Sclaudio 		    strcmp(RRDP_XMLNS, attr[i + 1]) == 0) {
638ecbadc1Sclaudio 			has_xmlns = 1;
648ecbadc1Sclaudio 			continue;
658ecbadc1Sclaudio 		}
668ecbadc1Sclaudio 		if (strcmp("version", attr[i]) == 0) {
678ecbadc1Sclaudio 			sxml->version = strtonum(attr[i + 1],
688ecbadc1Sclaudio 			    1, MAX_VERSION, &errstr);
698ecbadc1Sclaudio 			if (errstr == NULL)
708ecbadc1Sclaudio 				continue;
718ecbadc1Sclaudio 		}
7245735addSclaudio 		if (strcmp("session_id", attr[i]) == 0 &&
7345735addSclaudio 		    valid_uuid(attr[i + 1])) {
748ecbadc1Sclaudio 			sxml->session_id = xstrdup(attr[i + 1]);
758ecbadc1Sclaudio 			continue;
768ecbadc1Sclaudio 		}
778ecbadc1Sclaudio 		if (strcmp("serial", attr[i]) == 0) {
788ecbadc1Sclaudio 			sxml->serial = strtonum(attr[i + 1],
798ecbadc1Sclaudio 			    1, LLONG_MAX, &errstr);
808ecbadc1Sclaudio 			if (errstr == NULL)
818ecbadc1Sclaudio 				continue;
828ecbadc1Sclaudio 		}
838ecbadc1Sclaudio 		PARSE_FAIL(p,
848ecbadc1Sclaudio 		    "parse failed - non conforming "
85ac21c2b5Sclaudio 		    "attribute '%s' found in snapshot elem", attr[i]);
868ecbadc1Sclaudio 	}
878ecbadc1Sclaudio 	if (!(has_xmlns && sxml->version && sxml->session_id && sxml->serial))
888ecbadc1Sclaudio 		PARSE_FAIL(p,
898ecbadc1Sclaudio 		    "parse failed - incomplete snapshot attributes");
908ecbadc1Sclaudio 	if (strcmp(sxml->current->session_id, sxml->session_id) != 0)
918ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - session_id mismatch");
928ecbadc1Sclaudio 	if (sxml->current->serial != sxml->serial)
938ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - serial mismatch");
948ecbadc1Sclaudio 
958ecbadc1Sclaudio 	sxml->scope = SNAPSHOT_SCOPE_SNAPSHOT;
968ecbadc1Sclaudio }
978ecbadc1Sclaudio 
988ecbadc1Sclaudio static void
end_snapshot_elem(struct snapshot_xml * sxml)998ecbadc1Sclaudio end_snapshot_elem(struct snapshot_xml *sxml)
1008ecbadc1Sclaudio {
1018ecbadc1Sclaudio 	XML_Parser p = sxml->parser;
1028ecbadc1Sclaudio 
1038ecbadc1Sclaudio 	if (sxml->scope != SNAPSHOT_SCOPE_SNAPSHOT)
1048ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - exited snapshot "
1058ecbadc1Sclaudio 		    "elem unexpectedely");
1068ecbadc1Sclaudio 	sxml->scope = SNAPSHOT_SCOPE_END;
1078ecbadc1Sclaudio }
1088ecbadc1Sclaudio 
1098ecbadc1Sclaudio static void
start_publish_elem(struct snapshot_xml * sxml,const char ** attr)1108ecbadc1Sclaudio start_publish_elem(struct snapshot_xml *sxml, const char **attr)
1118ecbadc1Sclaudio {
1128ecbadc1Sclaudio 	XML_Parser p = sxml->parser;
1138ecbadc1Sclaudio 	char *uri = NULL;
1148ecbadc1Sclaudio 	int i, hasUri = 0;
1158ecbadc1Sclaudio 
1168ecbadc1Sclaudio 	if (sxml->scope != SNAPSHOT_SCOPE_SNAPSHOT)
1178ecbadc1Sclaudio 		PARSE_FAIL(p,
1188ecbadc1Sclaudio 		    "parse failed - entered publish elem unexpectedely");
1198ecbadc1Sclaudio 	for (i = 0; attr[i]; i += 2) {
1208ecbadc1Sclaudio 		if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
1218ecbadc1Sclaudio 			if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
1220610060dSjob 			    RSYNC_PROTO)) {
1238ecbadc1Sclaudio 				uri = xstrdup(attr[i + 1]);
1248ecbadc1Sclaudio 				continue;
1258ecbadc1Sclaudio 			}
1268ecbadc1Sclaudio 		}
1278ecbadc1Sclaudio 		/*
1288ecbadc1Sclaudio 		 * XXX it seems people can not write proper XML, ignore
1298ecbadc1Sclaudio 		 * bogus xmlns attribute on publish elements.
1308ecbadc1Sclaudio 		 */
1318ecbadc1Sclaudio 		if (strcmp("xmlns", attr[i]) == 0)
1328ecbadc1Sclaudio 			continue;
1338ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - non conforming"
1348ecbadc1Sclaudio 		    " attribute '%s' found in publish elem", attr[i]);
1358ecbadc1Sclaudio 	}
1368ecbadc1Sclaudio 	if (hasUri != 1)
1378ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - incomplete publish attributes");
1388ecbadc1Sclaudio 	sxml->pxml = new_publish_xml(PUB_ADD, uri, NULL, 0);
1398ecbadc1Sclaudio 	sxml->scope = SNAPSHOT_SCOPE_PUBLISH;
1408ecbadc1Sclaudio }
1418ecbadc1Sclaudio 
1428ecbadc1Sclaudio static void
end_publish_elem(struct snapshot_xml * sxml)1438ecbadc1Sclaudio end_publish_elem(struct snapshot_xml *sxml)
1448ecbadc1Sclaudio {
1458ecbadc1Sclaudio 	XML_Parser p = sxml->parser;
1468ecbadc1Sclaudio 
1478ecbadc1Sclaudio 	if (sxml->scope != SNAPSHOT_SCOPE_PUBLISH)
1488ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - exited publish "
1498ecbadc1Sclaudio 		    "elem unexpectedely");
1508ecbadc1Sclaudio 
1518ecbadc1Sclaudio 	if (publish_done(sxml->rrdp, sxml->pxml) != 0)
1528ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - bad publish elem");
1538ecbadc1Sclaudio 	sxml->pxml = NULL;
1548ecbadc1Sclaudio 
1558ecbadc1Sclaudio 	sxml->scope = SNAPSHOT_SCOPE_SNAPSHOT;
1568ecbadc1Sclaudio }
1578ecbadc1Sclaudio 
1588ecbadc1Sclaudio static void
snapshot_xml_elem_start(void * data,const char * el,const char ** attr)1598ecbadc1Sclaudio snapshot_xml_elem_start(void *data, const char *el, const char **attr)
1608ecbadc1Sclaudio {
1618ecbadc1Sclaudio 	struct snapshot_xml *sxml = data;
1628ecbadc1Sclaudio 	XML_Parser p = sxml->parser;
1638ecbadc1Sclaudio 
1648ecbadc1Sclaudio 	/*
1658ecbadc1Sclaudio 	 * Can only enter here once as we should have no ways to get back to
1668ecbadc1Sclaudio 	 * NONE scope
1678ecbadc1Sclaudio 	 */
1688ecbadc1Sclaudio 	if (strcmp("snapshot", el) == 0)
1698ecbadc1Sclaudio 		start_snapshot_elem(sxml, attr);
1708ecbadc1Sclaudio 	/*
1718ecbadc1Sclaudio 	 * Will enter here multiple times, BUT never nested. will start
1728ecbadc1Sclaudio 	 * collecting character data in that handler mem is cleared in end
1738ecbadc1Sclaudio 	 * block, (TODO or on parse failure)
1748ecbadc1Sclaudio 	 */
1758ecbadc1Sclaudio 	else if (strcmp("publish", el) == 0)
1768ecbadc1Sclaudio 		start_publish_elem(sxml, attr);
1778ecbadc1Sclaudio 	else
1788ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - unexpected elem exit found");
1798ecbadc1Sclaudio }
1808ecbadc1Sclaudio 
1818ecbadc1Sclaudio static void
snapshot_xml_elem_end(void * data,const char * el)1828ecbadc1Sclaudio snapshot_xml_elem_end(void *data, const char *el)
1838ecbadc1Sclaudio {
1848ecbadc1Sclaudio 	struct snapshot_xml *sxml = data;
1858ecbadc1Sclaudio 	XML_Parser p = sxml->parser;
1868ecbadc1Sclaudio 
1878ecbadc1Sclaudio 	if (strcmp("snapshot", el) == 0)
1888ecbadc1Sclaudio 		end_snapshot_elem(sxml);
1898ecbadc1Sclaudio 	else if (strcmp("publish", el) == 0)
1908ecbadc1Sclaudio 		end_publish_elem(sxml);
1918ecbadc1Sclaudio 	else
1928ecbadc1Sclaudio 		PARSE_FAIL(p, "parse failed - unexpected elem exit found");
1938ecbadc1Sclaudio }
1948ecbadc1Sclaudio 
1958ecbadc1Sclaudio static void
snapshot_content_handler(void * data,const char * content,int length)1968ecbadc1Sclaudio snapshot_content_handler(void *data, const char *content, int length)
1978ecbadc1Sclaudio {
1988ecbadc1Sclaudio 	struct snapshot_xml *sxml = data;
1991aea4e0eSclaudio 	XML_Parser p = sxml->parser;
2008ecbadc1Sclaudio 
2018ecbadc1Sclaudio 	if (sxml->scope == SNAPSHOT_SCOPE_PUBLISH)
2021aea4e0eSclaudio 		if (publish_add_content(sxml->pxml, content, length) == -1)
203*1a5078e9Sjob 			PARSE_FAIL(p, "parse failed, snapshot element for %s "
204*1a5078e9Sjob 			    "too big", sxml->pxml->uri);
2058ecbadc1Sclaudio }
2068ecbadc1Sclaudio 
2078487774dSclaudio static void
snapshot_doctype_handler(void * data,const char * doctypeName,const char * sysid,const char * pubid,int subset)2088487774dSclaudio snapshot_doctype_handler(void *data, const char *doctypeName,
2098487774dSclaudio     const char *sysid, const char *pubid, int subset)
2108487774dSclaudio {
2118487774dSclaudio 	struct snapshot_xml *sxml = data;
2128487774dSclaudio 	XML_Parser p = sxml->parser;
2138487774dSclaudio 
2148487774dSclaudio 	PARSE_FAIL(p, "parse failed - DOCTYPE not allowed");
2158487774dSclaudio }
2168487774dSclaudio 
2178ecbadc1Sclaudio struct snapshot_xml *
new_snapshot_xml(XML_Parser p,struct rrdp_session * rs,struct rrdp * r)2188ecbadc1Sclaudio new_snapshot_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r)
2198ecbadc1Sclaudio {
2208ecbadc1Sclaudio 	struct snapshot_xml *sxml;
2218ecbadc1Sclaudio 
2228ecbadc1Sclaudio 	if ((sxml = calloc(1, sizeof(*sxml))) == NULL)
2238ecbadc1Sclaudio 		err(1, "%s", __func__);
2248ecbadc1Sclaudio 	sxml->parser = p;
2258ecbadc1Sclaudio 	sxml->current = rs;
2268ecbadc1Sclaudio 	sxml->rrdp = r;
2278ecbadc1Sclaudio 
2288ecbadc1Sclaudio 	if (XML_ParserReset(sxml->parser, "US-ASCII") != XML_TRUE)
2298ecbadc1Sclaudio 		errx(1, "%s: XML_ParserReset failed", __func__);
2308ecbadc1Sclaudio 
2318ecbadc1Sclaudio 	XML_SetElementHandler(sxml->parser, snapshot_xml_elem_start,
2328ecbadc1Sclaudio 	    snapshot_xml_elem_end);
2338ecbadc1Sclaudio 	XML_SetCharacterDataHandler(sxml->parser, snapshot_content_handler);
2348ecbadc1Sclaudio 	XML_SetUserData(sxml->parser, sxml);
2358487774dSclaudio 	XML_SetDoctypeDeclHandler(sxml->parser, snapshot_doctype_handler,
2368487774dSclaudio 	    NULL);
2378ecbadc1Sclaudio 
2388ecbadc1Sclaudio 	return sxml;
2398ecbadc1Sclaudio }
2408ecbadc1Sclaudio 
2418ecbadc1Sclaudio void
free_snapshot_xml(struct snapshot_xml * sxml)2428ecbadc1Sclaudio free_snapshot_xml(struct snapshot_xml *sxml)
2438ecbadc1Sclaudio {
2448ecbadc1Sclaudio 	if (sxml == NULL)
2458ecbadc1Sclaudio 		return;
2468ecbadc1Sclaudio 
2478ecbadc1Sclaudio 	free(sxml->session_id);
2488ecbadc1Sclaudio 	free_publish_xml(sxml->pxml);
2498ecbadc1Sclaudio 	free(sxml);
2508ecbadc1Sclaudio }
2518ecbadc1Sclaudio 
25258d5f018Stb /* Used in regress. */
2538ecbadc1Sclaudio void
log_snapshot_xml(struct snapshot_xml * sxml)2548ecbadc1Sclaudio log_snapshot_xml(struct snapshot_xml *sxml)
2558ecbadc1Sclaudio {
2568ecbadc1Sclaudio 	logx("scope: %d", sxml->scope);
2578ecbadc1Sclaudio 	logx("version: %d", sxml->version);
2588ecbadc1Sclaudio 	logx("session_id: %s serial: %lld", sxml->session_id, sxml->serial);
2598ecbadc1Sclaudio }
260