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