1/*	$NetBSD: nsec3_50.c,v 1.1 2024/02/18 20:57:43 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*
17 * Copyright (C) 2004  Nominet, Ltd.
18 *
19 * Permission to use, copy, modify, and distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS" AND NOMINET DISCLAIMS ALL WARRANTIES WITH
24 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
25 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
26 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
27 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
28 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
29 * PERFORMANCE OF THIS SOFTWARE.
30 */
31
32/* RFC 5155 */
33
34#ifndef RDATA_GENERIC_NSEC3_50_C
35#define RDATA_GENERIC_NSEC3_50_C
36
37#include <isc/base32.h>
38#include <isc/iterated_hash.h>
39
40#define RRTYPE_NSEC3_ATTRIBUTES DNS_RDATATYPEATTR_DNSSEC
41
42static isc_result_t
43fromtext_nsec3(ARGS_FROMTEXT) {
44	isc_token_t token;
45	unsigned int flags;
46	unsigned char hashalg;
47	isc_buffer_t b;
48	unsigned char buf[256];
49
50	REQUIRE(type == dns_rdatatype_nsec3);
51
52	UNUSED(type);
53	UNUSED(rdclass);
54	UNUSED(callbacks);
55	UNUSED(origin);
56	UNUSED(options);
57
58	/* Hash. */
59	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
60				      false));
61	RETTOK(dns_hashalg_fromtext(&hashalg, &token.value.as_textregion));
62	RETERR(uint8_tobuffer(hashalg, target));
63
64	/* Flags. */
65	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
66				      false));
67	flags = token.value.as_ulong;
68	if (flags > 255U) {
69		RETTOK(ISC_R_RANGE);
70	}
71	RETERR(uint8_tobuffer(flags, target));
72
73	/* Iterations. */
74	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
75				      false));
76	if (token.value.as_ulong > 0xffffU) {
77		RETTOK(ISC_R_RANGE);
78	}
79	RETERR(uint16_tobuffer(token.value.as_ulong, target));
80
81	/* salt */
82	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
83				      false));
84	if (token.value.as_textregion.length > (255 * 2)) {
85		RETTOK(DNS_R_TEXTTOOLONG);
86	}
87	if (strcmp(DNS_AS_STR(token), "-") == 0) {
88		RETERR(uint8_tobuffer(0, target));
89	} else {
90		RETERR(uint8_tobuffer(strlen(DNS_AS_STR(token)) / 2, target));
91		RETERR(isc_hex_decodestring(DNS_AS_STR(token), target));
92	}
93
94	/*
95	 * Next hash a single base32hex word.
96	 */
97	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
98				      false));
99	isc_buffer_init(&b, buf, sizeof(buf));
100	RETTOK(isc_base32hexnp_decodestring(DNS_AS_STR(token), &b));
101	if (isc_buffer_usedlength(&b) > 0xffU) {
102		RETTOK(ISC_R_RANGE);
103	}
104	RETERR(uint8_tobuffer(isc_buffer_usedlength(&b), target));
105	RETERR(mem_tobuffer(target, &buf, isc_buffer_usedlength(&b)));
106
107	return (typemap_fromtext(lexer, target, true));
108}
109
110static isc_result_t
111totext_nsec3(ARGS_TOTEXT) {
112	isc_region_t sr;
113	unsigned int i, j;
114	unsigned char hash;
115	unsigned char flags;
116	char buf[sizeof("TYPE65535")];
117	uint32_t iterations;
118
119	REQUIRE(rdata->type == dns_rdatatype_nsec3);
120	REQUIRE(rdata->length != 0);
121
122	dns_rdata_toregion(rdata, &sr);
123
124	/* Hash */
125	hash = uint8_fromregion(&sr);
126	isc_region_consume(&sr, 1);
127	snprintf(buf, sizeof(buf), "%u ", hash);
128	RETERR(str_totext(buf, target));
129
130	/* Flags */
131	flags = uint8_fromregion(&sr);
132	isc_region_consume(&sr, 1);
133	snprintf(buf, sizeof(buf), "%u ", flags);
134	RETERR(str_totext(buf, target));
135
136	/* Iterations */
137	iterations = uint16_fromregion(&sr);
138	isc_region_consume(&sr, 2);
139	snprintf(buf, sizeof(buf), "%u ", iterations);
140	RETERR(str_totext(buf, target));
141
142	/* Salt */
143	j = uint8_fromregion(&sr);
144	isc_region_consume(&sr, 1);
145	INSIST(j <= sr.length);
146
147	if (j != 0) {
148		i = sr.length;
149		sr.length = j;
150		RETERR(isc_hex_totext(&sr, 1, "", target));
151		sr.length = i - j;
152	} else {
153		RETERR(str_totext("-", target));
154	}
155
156	if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
157		RETERR(str_totext(" (", target));
158	}
159	RETERR(str_totext(tctx->linebreak, target));
160
161	/* Next hash */
162	j = uint8_fromregion(&sr);
163	isc_region_consume(&sr, 1);
164	INSIST(j <= sr.length);
165
166	i = sr.length;
167	sr.length = j;
168	RETERR(isc_base32hexnp_totext(&sr, 1, "", target));
169	sr.length = i - j;
170
171	/*
172	 * Don't leave a trailing space when there's no typemap present.
173	 */
174	if (((tctx->flags & DNS_STYLEFLAG_MULTILINE) == 0) && (sr.length > 0)) {
175		RETERR(str_totext(" ", target));
176	}
177	RETERR(typemap_totext(&sr, tctx, target));
178
179	if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
180		RETERR(str_totext(" )", target));
181	}
182
183	return (ISC_R_SUCCESS);
184}
185
186static isc_result_t
187fromwire_nsec3(ARGS_FROMWIRE) {
188	isc_region_t sr, rr;
189	unsigned int saltlen, hashlen;
190
191	REQUIRE(type == dns_rdatatype_nsec3);
192
193	UNUSED(type);
194	UNUSED(rdclass);
195	UNUSED(options);
196	UNUSED(dctx);
197
198	isc_buffer_activeregion(source, &sr);
199	rr = sr;
200
201	/* hash(1), flags(1), iteration(2), saltlen(1) */
202	if (sr.length < 5U) {
203		RETERR(DNS_R_FORMERR);
204	}
205	saltlen = sr.base[4];
206	isc_region_consume(&sr, 5);
207
208	if (sr.length < saltlen) {
209		RETERR(DNS_R_FORMERR);
210	}
211	isc_region_consume(&sr, saltlen);
212
213	if (sr.length < 1U) {
214		RETERR(DNS_R_FORMERR);
215	}
216	hashlen = sr.base[0];
217	isc_region_consume(&sr, 1);
218
219	if (hashlen < 1 || sr.length < hashlen) {
220		RETERR(DNS_R_FORMERR);
221	}
222	isc_region_consume(&sr, hashlen);
223
224	RETERR(typemap_test(&sr, true));
225
226	RETERR(mem_tobuffer(target, rr.base, rr.length));
227	isc_buffer_forward(source, rr.length);
228	return (ISC_R_SUCCESS);
229}
230
231static isc_result_t
232towire_nsec3(ARGS_TOWIRE) {
233	isc_region_t sr;
234
235	REQUIRE(rdata->type == dns_rdatatype_nsec3);
236	REQUIRE(rdata->length != 0);
237
238	UNUSED(cctx);
239
240	dns_rdata_toregion(rdata, &sr);
241	return (mem_tobuffer(target, sr.base, sr.length));
242}
243
244static int
245compare_nsec3(ARGS_COMPARE) {
246	isc_region_t r1;
247	isc_region_t r2;
248
249	REQUIRE(rdata1->type == rdata2->type);
250	REQUIRE(rdata1->rdclass == rdata2->rdclass);
251	REQUIRE(rdata1->type == dns_rdatatype_nsec3);
252	REQUIRE(rdata1->length != 0);
253	REQUIRE(rdata2->length != 0);
254
255	dns_rdata_toregion(rdata1, &r1);
256	dns_rdata_toregion(rdata2, &r2);
257	return (isc_region_compare(&r1, &r2));
258}
259
260static isc_result_t
261fromstruct_nsec3(ARGS_FROMSTRUCT) {
262	dns_rdata_nsec3_t *nsec3 = source;
263	isc_region_t region;
264
265	REQUIRE(type == dns_rdatatype_nsec3);
266	REQUIRE(nsec3 != NULL);
267	REQUIRE(nsec3->common.rdtype == type);
268	REQUIRE(nsec3->common.rdclass == rdclass);
269	REQUIRE(nsec3->typebits != NULL || nsec3->len == 0);
270	REQUIRE(nsec3->hash == dns_hash_sha1);
271
272	UNUSED(type);
273	UNUSED(rdclass);
274
275	RETERR(uint8_tobuffer(nsec3->hash, target));
276	RETERR(uint8_tobuffer(nsec3->flags, target));
277	RETERR(uint16_tobuffer(nsec3->iterations, target));
278	RETERR(uint8_tobuffer(nsec3->salt_length, target));
279	RETERR(mem_tobuffer(target, nsec3->salt, nsec3->salt_length));
280	RETERR(uint8_tobuffer(nsec3->next_length, target));
281	RETERR(mem_tobuffer(target, nsec3->next, nsec3->next_length));
282
283	region.base = nsec3->typebits;
284	region.length = nsec3->len;
285	RETERR(typemap_test(&region, true));
286	return (mem_tobuffer(target, nsec3->typebits, nsec3->len));
287}
288
289static isc_result_t
290tostruct_nsec3(ARGS_TOSTRUCT) {
291	isc_region_t region;
292	dns_rdata_nsec3_t *nsec3 = target;
293
294	REQUIRE(rdata->type == dns_rdatatype_nsec3);
295	REQUIRE(nsec3 != NULL);
296	REQUIRE(rdata->length != 0);
297
298	nsec3->common.rdclass = rdata->rdclass;
299	nsec3->common.rdtype = rdata->type;
300	ISC_LINK_INIT(&nsec3->common, link);
301
302	region.base = rdata->data;
303	region.length = rdata->length;
304	nsec3->hash = uint8_consume_fromregion(&region);
305	nsec3->flags = uint8_consume_fromregion(&region);
306	nsec3->iterations = uint16_consume_fromregion(&region);
307
308	nsec3->salt_length = uint8_consume_fromregion(&region);
309	INSIST(nsec3->salt_length <= region.length);
310	nsec3->salt = mem_maybedup(mctx, region.base, nsec3->salt_length);
311	if (nsec3->salt == NULL) {
312		return (ISC_R_NOMEMORY);
313	}
314	isc_region_consume(&region, nsec3->salt_length);
315
316	nsec3->next_length = uint8_consume_fromregion(&region);
317	INSIST(nsec3->next_length <= region.length);
318	nsec3->next = mem_maybedup(mctx, region.base, nsec3->next_length);
319	if (nsec3->next == NULL) {
320		goto cleanup;
321	}
322	isc_region_consume(&region, nsec3->next_length);
323
324	nsec3->len = region.length;
325	nsec3->typebits = mem_maybedup(mctx, region.base, region.length);
326	if (nsec3->typebits == NULL) {
327		goto cleanup;
328	}
329
330	nsec3->mctx = mctx;
331	return (ISC_R_SUCCESS);
332
333cleanup:
334	if (nsec3->next != NULL) {
335		isc_mem_free(mctx, nsec3->next);
336	}
337	isc_mem_free(mctx, nsec3->salt);
338	return (ISC_R_NOMEMORY);
339}
340
341static void
342freestruct_nsec3(ARGS_FREESTRUCT) {
343	dns_rdata_nsec3_t *nsec3 = source;
344
345	REQUIRE(nsec3 != NULL);
346	REQUIRE(nsec3->common.rdtype == dns_rdatatype_nsec3);
347
348	if (nsec3->mctx == NULL) {
349		return;
350	}
351
352	if (nsec3->salt != NULL) {
353		isc_mem_free(nsec3->mctx, nsec3->salt);
354	}
355	if (nsec3->next != NULL) {
356		isc_mem_free(nsec3->mctx, nsec3->next);
357	}
358	if (nsec3->typebits != NULL) {
359		isc_mem_free(nsec3->mctx, nsec3->typebits);
360	}
361	nsec3->mctx = NULL;
362}
363
364static isc_result_t
365additionaldata_nsec3(ARGS_ADDLDATA) {
366	REQUIRE(rdata->type == dns_rdatatype_nsec3);
367
368	UNUSED(rdata);
369	UNUSED(add);
370	UNUSED(arg);
371
372	return (ISC_R_SUCCESS);
373}
374
375static isc_result_t
376digest_nsec3(ARGS_DIGEST) {
377	isc_region_t r;
378
379	REQUIRE(rdata->type == dns_rdatatype_nsec3);
380
381	dns_rdata_toregion(rdata, &r);
382	return ((digest)(arg, &r));
383}
384
385static bool
386checkowner_nsec3(ARGS_CHECKOWNER) {
387	unsigned char owner[NSEC3_MAX_HASH_LENGTH];
388	isc_buffer_t buffer;
389	dns_label_t label;
390
391	REQUIRE(type == dns_rdatatype_nsec3);
392
393	UNUSED(type);
394	UNUSED(rdclass);
395	UNUSED(wildcard);
396
397	/*
398	 * First label is a base32hex string without padding.
399	 */
400	dns_name_getlabel(name, 0, &label);
401	isc_region_consume(&label, 1);
402	isc_buffer_init(&buffer, owner, sizeof(owner));
403	if (isc_base32hexnp_decoderegion(&label, &buffer) == ISC_R_SUCCESS) {
404		return (true);
405	}
406
407	return (false);
408}
409
410static bool
411checknames_nsec3(ARGS_CHECKNAMES) {
412	REQUIRE(rdata->type == dns_rdatatype_nsec3);
413
414	UNUSED(rdata);
415	UNUSED(owner);
416	UNUSED(bad);
417
418	return (true);
419}
420
421static int
422casecompare_nsec3(ARGS_COMPARE) {
423	return (compare_nsec3(rdata1, rdata2));
424}
425
426#endif /* RDATA_GENERIC_NSEC3_50_C */
427