1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: MPL-2.0
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 #if HAVE_CMOCKA
15 
16 #include <sched.h> /* IWYU pragma: keep */
17 #include <setjmp.h>
18 #include <stdarg.h>
19 #include <stddef.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #define UNIT_TESTING
25 #include <cmocka.h>
26 
27 #include <isc/hex.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
30 
31 #include <dns/db.h>
32 #include <dns/nsec3.h>
33 #include <dns/result.h>
34 
35 #include "../zone_p.h"
36 #include "dnstest.h"
37 
38 #define HASH	1
39 #define FLAGS	0
40 #define ITER	5
41 #define SALTLEN 4
42 #define SALT	"FEDCBA98"
43 
44 static int
_setup(void ** state)45 _setup(void **state) {
46 	isc_result_t result;
47 
48 	UNUSED(state);
49 
50 	result = dns_test_begin(NULL, false);
51 	assert_int_equal(result, ISC_R_SUCCESS);
52 
53 	return (0);
54 }
55 
56 static int
_teardown(void ** state)57 _teardown(void **state) {
58 	UNUSED(state);
59 
60 	dns_test_end();
61 
62 	return (0);
63 }
64 
65 /*%
66  * Structures containing parameters for nsec3param_salttotext_test().
67  */
68 typedef struct {
69 	dns_hash_t hash;
70 	unsigned char flags;
71 	dns_iterations_t iterations;
72 	unsigned char salt_length;
73 	const char *salt;
74 } nsec3param_rdata_test_params_t;
75 
76 typedef struct {
77 	nsec3param_rdata_test_params_t lookup;
78 	nsec3param_rdata_test_params_t expect;
79 	bool resalt;
80 	isc_result_t expected_result;
81 } nsec3param_change_test_params_t;
82 
83 static void
decode_salt(const char * string,unsigned char * salt,size_t saltlen)84 decode_salt(const char *string, unsigned char *salt, size_t saltlen) {
85 	isc_buffer_t buf;
86 	isc_result_t result;
87 
88 	isc_buffer_init(&buf, salt, saltlen);
89 	result = isc_hex_decodestring(string, &buf);
90 	assert_int_equal(result, ISC_R_SUCCESS);
91 }
92 
93 static void
copy_params(nsec3param_rdata_test_params_t from,dns_rdata_nsec3param_t * to,unsigned char * saltbuf,size_t saltlen)94 copy_params(nsec3param_rdata_test_params_t from, dns_rdata_nsec3param_t *to,
95 	    unsigned char *saltbuf, size_t saltlen) {
96 	to->hash = from.hash;
97 	to->flags = from.flags;
98 	to->iterations = from.iterations;
99 	to->salt_length = from.salt_length;
100 	if (from.salt == NULL) {
101 		to->salt = NULL;
102 	} else if (strcmp(from.salt, "-") == 0) {
103 		DE_CONST("-", to->salt);
104 	} else {
105 		decode_salt(from.salt, saltbuf, saltlen);
106 		to->salt = saltbuf;
107 	}
108 }
109 
110 static nsec3param_rdata_test_params_t
rdata_fromparams(uint8_t hash,uint8_t flags,uint16_t iter,uint8_t saltlen,const char * salt)111 rdata_fromparams(uint8_t hash, uint8_t flags, uint16_t iter, uint8_t saltlen,
112 		 const char *salt) {
113 	nsec3param_rdata_test_params_t nsec3param;
114 	nsec3param.hash = hash;
115 	nsec3param.flags = flags;
116 	nsec3param.iterations = iter;
117 	nsec3param.salt_length = saltlen;
118 	nsec3param.salt = salt;
119 	return (nsec3param);
120 }
121 
122 /*%
123  * Check whether zone_lookup_nsec3param() finds the correct NSEC3PARAM
124  * and sets the correct parameters to use in dns_zone_setnsec3param().
125  */
126 static void
nsec3param_change_test(const nsec3param_change_test_params_t * test)127 nsec3param_change_test(const nsec3param_change_test_params_t *test) {
128 	dns_zone_t *zone = NULL;
129 	dns_rdata_nsec3param_t param, lookup, expect;
130 	isc_result_t result;
131 	unsigned char lookupsalt[255];
132 	unsigned char expectsalt[255];
133 	unsigned char saltbuf[255];
134 
135 	/*
136 	 * Prepare a zone along with its signing keys.
137 	 */
138 	result = dns_test_makezone("nsec3", &zone, NULL, false);
139 	assert_int_equal(result, ISC_R_SUCCESS);
140 
141 	result = dns_zone_setfile(zone, "testdata/nsec3param/nsec3.db.signed",
142 				  dns_masterformat_text,
143 				  &dns_master_style_default);
144 	assert_int_equal(result, ISC_R_SUCCESS);
145 
146 	result = dns_zone_load(zone, false);
147 	assert_int_equal(result, ISC_R_SUCCESS);
148 
149 	/*
150 	 * Copy parameters.
151 	 */
152 	copy_params(test->lookup, &lookup, lookupsalt, sizeof(lookupsalt));
153 	copy_params(test->expect, &expect, expectsalt, sizeof(expectsalt));
154 
155 	/*
156 	 * Test dns__zone_lookup_nsec3param().
157 	 */
158 	result = dns__zone_lookup_nsec3param(zone, &lookup, &param, saltbuf,
159 					     test->resalt);
160 	assert_int_equal(result, test->expected_result);
161 	assert_int_equal(param.hash, expect.hash);
162 	assert_int_equal(param.flags, expect.flags);
163 	assert_int_equal(param.iterations, expect.iterations);
164 	assert_int_equal(param.salt_length, expect.salt_length);
165 	assert_non_null(param.salt);
166 	if (expect.salt != NULL) {
167 		int ret = memcmp(param.salt, expect.salt, expect.salt_length);
168 		assert_true(ret == 0);
169 	} else {
170 		/*
171 		 * We don't know what the new salt is, but we can compare it
172 		 * to the previous salt and test that it has changed.
173 		 */
174 		unsigned char salt[SALTLEN];
175 		int ret;
176 		decode_salt(SALT, salt, SALTLEN);
177 		ret = memcmp(param.salt, salt, SALTLEN);
178 		assert_false(ret == 0);
179 	}
180 
181 	/*
182 	 * Detach.
183 	 */
184 	dns_zone_detach(&zone);
185 }
186 
187 static void
nsec3param_change(void ** state)188 nsec3param_change(void **state) {
189 	size_t i;
190 
191 	/*
192 	 * Define tests.
193 	 */
194 	const nsec3param_change_test_params_t tests[] = {
195 		/*
196 		 * 1. Change nothing (don't care about salt).
197 		 * This should return ISC_R_SUCCESS because we are already
198 		 * using these NSEC3 parameters.
199 		 */
200 		{ rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, NULL),
201 		  rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, SALT), false,
202 		  ISC_R_SUCCESS },
203 		/*
204 		 * 2. Change nothing, but force a resalt.
205 		 * This should change the salt. Set 'expect.salt' to NULL to
206 		 * test a new salt has been generated.
207 		 */
208 		{ rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, NULL),
209 		  rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, NULL), true,
210 		  DNS_R_NSEC3RESALT },
211 		/*
212 		 * 3. Change iterations.
213 		 * The NSEC3 paarameters are not found, and there is no
214 		 * need to resalt because an explicit salt has been set,
215 		 * and resalt is not enforced.
216 		 */
217 		{ rdata_fromparams(HASH, FLAGS, 10, SALTLEN, SALT),
218 		  rdata_fromparams(HASH, FLAGS, 10, SALTLEN, SALT), false,
219 		  ISC_R_NOTFOUND },
220 		/*
221 		 * 4. Change iterations, don't care about the salt.
222 		 * We don't care about the salt. Since we need to change the
223 		 * NSEC3 parameters, we will also resalt.
224 		 */
225 		{ rdata_fromparams(HASH, FLAGS, 10, SALTLEN, NULL),
226 		  rdata_fromparams(HASH, FLAGS, 10, SALTLEN, NULL), false,
227 		  DNS_R_NSEC3RESALT },
228 		/*
229 		 * 5. Change salt length.
230 		 * Changing salt length means we need to resalt.
231 		 */
232 		{ rdata_fromparams(HASH, FLAGS, ITER, 16, NULL),
233 		  rdata_fromparams(HASH, FLAGS, ITER, 16, NULL), false,
234 		  DNS_R_NSEC3RESALT },
235 		/*
236 		 * 6. Set explicit salt.
237 		 * A different salt, so the NSEC3 parameters are not found.
238 		 * No need to resalt because an explicit salt is available.
239 		 */
240 		{ rdata_fromparams(HASH, FLAGS, ITER, 4, "12345678"),
241 		  rdata_fromparams(HASH, FLAGS, ITER, 4, "12345678"), false,
242 		  ISC_R_NOTFOUND },
243 		/*
244 		 * 7. Same salt.
245 		 * Nothing changed, so expect ISC_R_SUCCESS as a result.
246 		 */
247 		{ rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, SALT),
248 		  rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, SALT), false,
249 		  ISC_R_SUCCESS },
250 		/*
251 		 * 8. Same salt, and force resalt.
252 		 * Nothing changed, but a resalt is enforced.
253 		 */
254 		{ rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, SALT),
255 		  rdata_fromparams(HASH, FLAGS, ITER, SALTLEN, NULL), true,
256 		  DNS_R_NSEC3RESALT },
257 		/*
258 		 * 9. No salt.
259 		 * Change parameters to use no salt. These parameters are
260 		 * not found, and no new salt needs to be generated.
261 		 */
262 		{ rdata_fromparams(HASH, FLAGS, ITER, 0, NULL),
263 		  rdata_fromparams(HASH, FLAGS, ITER, 0, "-"), true,
264 		  ISC_R_NOTFOUND },
265 		/*
266 		 * 10. No salt, explicit.
267 		 * Same as above, but set no salt explicitly.
268 		 */
269 		{ rdata_fromparams(HASH, FLAGS, ITER, 0, "-"),
270 		  rdata_fromparams(HASH, FLAGS, ITER, 0, "-"), true,
271 		  ISC_R_NOTFOUND },
272 	};
273 
274 	UNUSED(state);
275 
276 	/*
277 	 * Run tests.
278 	 */
279 	for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
280 		nsec3param_change_test(&tests[i]);
281 	}
282 }
283 
284 int
main(void)285 main(void) {
286 	const struct CMUnitTest tests[] = {
287 		cmocka_unit_test_setup_teardown(nsec3param_change, _setup,
288 						_teardown),
289 	};
290 
291 	return (cmocka_run_group_tests(tests, NULL, NULL));
292 }
293 
294 #else /* HAVE_CMOCKA */
295 
296 #include <stdio.h>
297 
298 int
main(void)299 main(void) {
300 	printf("1..0 # Skipped: cmocka not available\n");
301 	return (SKIPPED_TEST_EXIT_CODE);
302 }
303 
304 #endif /* if HAVE_CMOCKA */
305