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