xref: /openbsd/usr.sbin/rpki-client/rrdp_delta.c (revision 1a5078e9)
1 /*	$OpenBSD: rrdp_delta.c,v 1.14 2024/05/30 09:54:59 job Exp $ */
2 /*
3  * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
4  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <err.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 
26 #include <expat.h>
27 #include <openssl/sha.h>
28 
29 #include "extern.h"
30 #include "rrdp.h"
31 
32 enum delta_scope {
33 	DELTA_SCOPE_NONE,
34 	DELTA_SCOPE_EMPTY_DELTA,
35 	DELTA_SCOPE_DELTA,
36 	DELTA_SCOPE_PUBLISH,
37 	DELTA_SCOPE_END
38 };
39 
40 struct delta_xml {
41 	XML_Parser		 parser;
42 	struct rrdp_session	*current;
43 	struct rrdp		*rrdp;
44 	struct publish_xml	*pxml;
45 	char			*session_id;
46 	long long		 serial;
47 	int			 version;
48 	enum delta_scope	 scope;
49 };
50 
51 static void
start_delta_elem(struct delta_xml * dxml,const char ** attr)52 start_delta_elem(struct delta_xml *dxml, const char **attr)
53 {
54 	XML_Parser p = dxml->parser;
55 	int has_xmlns = 0;
56 	int i;
57 
58 	if (dxml->scope != DELTA_SCOPE_NONE)
59 		PARSE_FAIL(p,
60 		    "parse failed - entered delta elem unexpectedely");
61 	for (i = 0; attr[i]; i += 2) {
62 		const char *errstr;
63 		if (strcmp("xmlns", attr[i]) == 0 &&
64 		    strcmp(RRDP_XMLNS, attr[i + 1]) == 0) {
65 			has_xmlns = 1;
66 			continue;
67 		}
68 		if (strcmp("version", attr[i]) == 0) {
69 			dxml->version = strtonum(attr[i + 1],
70 			    1, MAX_VERSION, &errstr);
71 			if (errstr == NULL)
72 				continue;
73 		}
74 		if (strcmp("session_id", attr[i]) == 0 &&
75 		    valid_uuid(attr[i + 1])) {
76 			dxml->session_id = xstrdup(attr[i + 1]);
77 			continue;
78 		}
79 		if (strcmp("serial", attr[i]) == 0) {
80 			dxml->serial = strtonum(attr[i + 1],
81 			    1, LLONG_MAX, &errstr);
82 			if (errstr == NULL)
83 				continue;
84 		}
85 		PARSE_FAIL(p, "parse failed - non conforming "
86 		    "attribute '%s' found in delta elem", attr[i]);
87 	}
88 	if (!(has_xmlns && dxml->version && dxml->session_id && dxml->serial))
89 		PARSE_FAIL(p, "parse failed - incomplete delta attributes");
90 	if (strcmp(dxml->current->session_id, dxml->session_id) != 0)
91 		PARSE_FAIL(p, "parse failed - session_id mismatch");
92 	if (dxml->current->serial != dxml->serial)
93 		PARSE_FAIL(p, "parse failed - serial mismatch");
94 
95 	dxml->scope = DELTA_SCOPE_EMPTY_DELTA;
96 }
97 
98 static void
end_delta_elem(struct delta_xml * dxml)99 end_delta_elem(struct delta_xml *dxml)
100 {
101 	XML_Parser p = dxml->parser;
102 
103 	if (dxml->scope == DELTA_SCOPE_EMPTY_DELTA)
104 		PARSE_FAIL(p, "parse failed - empty delta");
105 	if (dxml->scope != DELTA_SCOPE_DELTA)
106 		PARSE_FAIL(p, "parse failed - exited delta "
107 		    "elem unexpectedely");
108 	dxml->scope = DELTA_SCOPE_END;
109 }
110 
111 static void
start_publish_withdraw_elem(struct delta_xml * dxml,const char ** attr,int withdraw)112 start_publish_withdraw_elem(struct delta_xml *dxml, const char **attr,
113     int withdraw)
114 {
115 	XML_Parser p = dxml->parser;
116 	char *uri = NULL, hash[SHA256_DIGEST_LENGTH];
117 	int i, hasUri = 0, hasHash = 0;
118 	enum publish_type pub = PUB_UPD;
119 
120 	if (dxml->scope != DELTA_SCOPE_EMPTY_DELTA &&
121 	    dxml->scope != DELTA_SCOPE_DELTA)
122 		PARSE_FAIL(p, "parse failed - entered publish/withdraw "
123 		    "elem unexpectedely");
124 	for (i = 0; attr[i]; i += 2) {
125 		if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
126 			if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
127 			    RSYNC_PROTO)) {
128 				uri = xstrdup(attr[i + 1]);
129 				continue;
130 			}
131 		}
132 		if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) {
133 			if (hex_decode(attr[i + 1], hash, sizeof(hash)) == 0)
134 				continue;
135 		}
136 		PARSE_FAIL(p, "parse failed - non conforming "
137 		    "attribute '%s' found in publish/withdraw elem", attr[i]);
138 	}
139 	if (hasUri != 1)
140 		PARSE_FAIL(p,
141 		    "parse failed - incomplete publish/withdraw attributes");
142 	if (withdraw && hasHash != 1)
143 		PARSE_FAIL(p, "parse failed - incomplete withdraw attributes");
144 
145 	if (withdraw)
146 		pub = PUB_DEL;
147 	else if (hasHash == 0)
148 		pub = PUB_ADD;
149 	dxml->pxml = new_publish_xml(pub, uri, hash,
150 	    hasHash ? sizeof(hash) : 0);
151 	dxml->scope = DELTA_SCOPE_PUBLISH;
152 }
153 
154 static void
end_publish_withdraw_elem(struct delta_xml * dxml,int withdraw)155 end_publish_withdraw_elem(struct delta_xml *dxml, int withdraw)
156 {
157 	XML_Parser p = dxml->parser;
158 
159 	if (dxml->scope != DELTA_SCOPE_PUBLISH)
160 		PARSE_FAIL(p, "parse failed - exited publish/withdraw "
161 		    "elem unexpectedely");
162 
163 	if (publish_done(dxml->rrdp, dxml->pxml) != 0)
164 		PARSE_FAIL(p, "parse failed - bad publish/withdraw elem");
165 	dxml->pxml = NULL;
166 
167 	dxml->scope = DELTA_SCOPE_DELTA;
168 }
169 
170 static void
delta_xml_elem_start(void * data,const char * el,const char ** attr)171 delta_xml_elem_start(void *data, const char *el, const char **attr)
172 {
173 	struct delta_xml *dxml = data;
174 	XML_Parser p = dxml->parser;
175 
176 	/*
177 	 * Can only enter here once as we should have no ways to get back to
178 	 * NONE scope
179 	 */
180 	if (strcmp("delta", el) == 0)
181 		start_delta_elem(dxml, attr);
182 	/*
183 	 * Will enter here multiple times, BUT never nested. will start
184 	 * collecting character data in that handler
185 	 * mem is cleared in end block, (TODO or on parse failure)
186 	 */
187 	else if (strcmp("publish", el) == 0)
188 		start_publish_withdraw_elem(dxml, attr, 0);
189 	else if (strcmp("withdraw", el) == 0)
190 		start_publish_withdraw_elem(dxml, attr, 1);
191 	else
192 		PARSE_FAIL(p, "parse failed - unexpected elem exit found");
193 }
194 
195 static void
delta_xml_elem_end(void * data,const char * el)196 delta_xml_elem_end(void *data, const char *el)
197 {
198 	struct delta_xml *dxml = data;
199 	XML_Parser p = dxml->parser;
200 
201 	if (strcmp("delta", el) == 0)
202 		end_delta_elem(dxml);
203 	/*
204 	 * TODO does this allow <publish></withdraw> or is that caught by basic
205 	 * xml parsing
206 	 */
207 	else if (strcmp("publish", el) == 0)
208 		end_publish_withdraw_elem(dxml, 0);
209 	else if (strcmp("withdraw", el) == 0)
210 		end_publish_withdraw_elem(dxml, 1);
211 	else
212 		PARSE_FAIL(p, "parse failed - unexpected elem exit found");
213 }
214 
215 static void
delta_content_handler(void * data,const char * content,int length)216 delta_content_handler(void *data, const char *content, int length)
217 {
218 	struct delta_xml *dxml = data;
219 	XML_Parser p = dxml->parser;
220 
221 	if (dxml->scope == DELTA_SCOPE_PUBLISH)
222 		if (publish_add_content(dxml->pxml, content, length) == -1)
223 			PARSE_FAIL(p, "parse failed, delta element for %s too "
224 			    "big", dxml->pxml->uri);
225 }
226 
227 static void
delta_doctype_handler(void * data,const char * doctypeName,const char * sysid,const char * pubid,int subset)228 delta_doctype_handler(void *data, const char *doctypeName,
229     const char *sysid, const char *pubid, int subset)
230 {
231 	struct delta_xml *dxml = data;
232 	XML_Parser p = dxml->parser;
233 
234 	PARSE_FAIL(p, "parse failed - DOCTYPE not allowed");
235 }
236 
237 struct delta_xml *
new_delta_xml(XML_Parser p,struct rrdp_session * rs,struct rrdp * r)238 new_delta_xml(XML_Parser p, struct rrdp_session *rs, struct rrdp *r)
239 {
240 	struct delta_xml *dxml;
241 
242 	if ((dxml = calloc(1, sizeof(*dxml))) == NULL)
243 		err(1, "%s", __func__);
244 	dxml->parser = p;
245 	dxml->current = rs;
246 	dxml->rrdp = r;
247 
248 	if (XML_ParserReset(dxml->parser, "US-ASCII") != XML_TRUE)
249 		errx(1, "%s: XML_ParserReset failed", __func__);
250 
251 	XML_SetElementHandler(dxml->parser, delta_xml_elem_start,
252 	    delta_xml_elem_end);
253 	XML_SetCharacterDataHandler(dxml->parser, delta_content_handler);
254 	XML_SetUserData(dxml->parser, dxml);
255 	XML_SetDoctypeDeclHandler(dxml->parser, delta_doctype_handler, NULL);
256 
257 	return dxml;
258 }
259 
260 void
free_delta_xml(struct delta_xml * dxml)261 free_delta_xml(struct delta_xml *dxml)
262 {
263 	if (dxml == NULL)
264 		return;
265 
266 	free(dxml->session_id);
267 	free_publish_xml(dxml->pxml);
268 	free(dxml);
269 }
270 
271 /* Used in regress. */
272 void
log_delta_xml(struct delta_xml * dxml)273 log_delta_xml(struct delta_xml *dxml)
274 {
275 	logx("version: %d", dxml->version);
276 	logx("session_id: %s serial: %lld", dxml->session_id, dxml->serial);
277 }
278