xref: /freebsd/contrib/unbound/util/edns.c (revision 5685098846d7f11ad642d9804d94dc7429a7b212)
14c75e3aaSDag-Erling Smørgrav /*
24c75e3aaSDag-Erling Smørgrav  * util/edns.c - handle base EDNS options.
34c75e3aaSDag-Erling Smørgrav  *
44c75e3aaSDag-Erling Smørgrav  * Copyright (c) 2018, NLnet Labs. All rights reserved.
54c75e3aaSDag-Erling Smørgrav  *
64c75e3aaSDag-Erling Smørgrav  * This software is open source.
74c75e3aaSDag-Erling Smørgrav  *
84c75e3aaSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
94c75e3aaSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
104c75e3aaSDag-Erling Smørgrav  * are met:
114c75e3aaSDag-Erling Smørgrav  *
124c75e3aaSDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
134c75e3aaSDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
144c75e3aaSDag-Erling Smørgrav  *
154c75e3aaSDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
164c75e3aaSDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
174c75e3aaSDag-Erling Smørgrav  * and/or other materials provided with the distribution.
184c75e3aaSDag-Erling Smørgrav  *
194c75e3aaSDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
204c75e3aaSDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
214c75e3aaSDag-Erling Smørgrav  * specific prior written permission.
224c75e3aaSDag-Erling Smørgrav  *
234c75e3aaSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
244c75e3aaSDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
254c75e3aaSDag-Erling Smørgrav  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
264c75e3aaSDag-Erling Smørgrav  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
274c75e3aaSDag-Erling Smørgrav  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
284c75e3aaSDag-Erling Smørgrav  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
294c75e3aaSDag-Erling Smørgrav  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
304c75e3aaSDag-Erling Smørgrav  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
314c75e3aaSDag-Erling Smørgrav  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
324c75e3aaSDag-Erling Smørgrav  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
334c75e3aaSDag-Erling Smørgrav  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
344c75e3aaSDag-Erling Smørgrav  */
354c75e3aaSDag-Erling Smørgrav 
364c75e3aaSDag-Erling Smørgrav /**
374c75e3aaSDag-Erling Smørgrav  * \file
384c75e3aaSDag-Erling Smørgrav  *
394c75e3aaSDag-Erling Smørgrav  * This file contains functions for base EDNS options.
404c75e3aaSDag-Erling Smørgrav  */
414c75e3aaSDag-Erling Smørgrav 
424c75e3aaSDag-Erling Smørgrav #include "config.h"
43e86b9096SDag-Erling Smørgrav #include "util/edns.h"
444c75e3aaSDag-Erling Smørgrav #include "util/config_file.h"
454c75e3aaSDag-Erling Smørgrav #include "util/netevent.h"
46c0caa2e2SCy Schubert #include "util/net_help.h"
474c75e3aaSDag-Erling Smørgrav #include "util/regional.h"
488f76bb7dSCy Schubert #include "util/rfc_1982.h"
498f76bb7dSCy Schubert #include "util/siphash.h"
504c75e3aaSDag-Erling Smørgrav #include "util/data/msgparse.h"
514c75e3aaSDag-Erling Smørgrav #include "util/data/msgreply.h"
528f76bb7dSCy Schubert #include "sldns/sbuffer.h"
534c75e3aaSDag-Erling Smørgrav 
54c0caa2e2SCy Schubert #if 0
55c0caa2e2SCy Schubert /* XXX: remove me */
564c75e3aaSDag-Erling Smørgrav #include "edns.h"
57c0caa2e2SCy Schubert #endif
58c0caa2e2SCy Schubert 
edns_strings_create(void)59369c6923SCy Schubert struct edns_strings* edns_strings_create(void)
60c0caa2e2SCy Schubert {
61369c6923SCy Schubert 	struct edns_strings* edns_strings = calloc(1,
62369c6923SCy Schubert 		sizeof(struct edns_strings));
63369c6923SCy Schubert 	if(!edns_strings)
64c0caa2e2SCy Schubert 		return NULL;
65369c6923SCy Schubert 	if(!(edns_strings->region = regional_create())) {
66369c6923SCy Schubert 		edns_strings_delete(edns_strings);
67c0caa2e2SCy Schubert 		return NULL;
68c0caa2e2SCy Schubert 	}
69369c6923SCy Schubert 	return edns_strings;
70c0caa2e2SCy Schubert }
71c0caa2e2SCy Schubert 
edns_strings_delete(struct edns_strings * edns_strings)72369c6923SCy Schubert void edns_strings_delete(struct edns_strings* edns_strings)
73c0caa2e2SCy Schubert {
74369c6923SCy Schubert 	if(!edns_strings)
75c0caa2e2SCy Schubert 		return;
76369c6923SCy Schubert 	regional_destroy(edns_strings->region);
77369c6923SCy Schubert 	free(edns_strings);
78c0caa2e2SCy Schubert }
79c0caa2e2SCy Schubert 
80c0caa2e2SCy Schubert static int
edns_strings_client_insert(struct edns_strings * edns_strings,struct sockaddr_storage * addr,socklen_t addrlen,int net,const char * string)81369c6923SCy Schubert edns_strings_client_insert(struct edns_strings* edns_strings,
82c0caa2e2SCy Schubert 	struct sockaddr_storage* addr, socklen_t addrlen, int net,
83369c6923SCy Schubert 	const char* string)
84c0caa2e2SCy Schubert {
85369c6923SCy Schubert 	struct edns_string_addr* esa = regional_alloc_zero(edns_strings->region,
86369c6923SCy Schubert 		sizeof(struct edns_string_addr));
87369c6923SCy Schubert 	if(!esa)
88c0caa2e2SCy Schubert 		return 0;
89369c6923SCy Schubert 	esa->string_len = strlen(string);
90369c6923SCy Schubert 	esa->string = regional_alloc_init(edns_strings->region, string,
91369c6923SCy Schubert 		esa->string_len);
92369c6923SCy Schubert 	if(!esa->string)
93369c6923SCy Schubert 		return 0;
94369c6923SCy Schubert 	if(!addr_tree_insert(&edns_strings->client_strings, &esa->node, addr,
95369c6923SCy Schubert 		addrlen, net)) {
96369c6923SCy Schubert 		verbose(VERB_QUERY, "duplicate EDNS client string ignored.");
97c0caa2e2SCy Schubert 	}
98c0caa2e2SCy Schubert 	return 1;
99c0caa2e2SCy Schubert }
100c0caa2e2SCy Schubert 
edns_strings_apply_cfg(struct edns_strings * edns_strings,struct config_file * config)101369c6923SCy Schubert int edns_strings_apply_cfg(struct edns_strings* edns_strings,
102c0caa2e2SCy Schubert 	struct config_file* config)
103c0caa2e2SCy Schubert {
104c0caa2e2SCy Schubert 	struct config_str2list* c;
105369c6923SCy Schubert 	regional_free_all(edns_strings->region);
106369c6923SCy Schubert 	addr_tree_init(&edns_strings->client_strings);
107c0caa2e2SCy Schubert 
108369c6923SCy Schubert 	for(c=config->edns_client_strings; c; c=c->next) {
109c0caa2e2SCy Schubert 		struct sockaddr_storage addr;
110c0caa2e2SCy Schubert 		socklen_t addrlen;
111c0caa2e2SCy Schubert 		int net;
112c0caa2e2SCy Schubert 		log_assert(c->str && c->str2);
113c0caa2e2SCy Schubert 
114c0caa2e2SCy Schubert 		if(!netblockstrtoaddr(c->str, UNBOUND_DNS_PORT, &addr, &addrlen,
115c0caa2e2SCy Schubert 			&net)) {
116369c6923SCy Schubert 			log_err("cannot parse EDNS client string IP netblock: "
117369c6923SCy Schubert 				"%s", c->str);
118c0caa2e2SCy Schubert 			return 0;
119c0caa2e2SCy Schubert 		}
120369c6923SCy Schubert 		if(!edns_strings_client_insert(edns_strings, &addr, addrlen,
121369c6923SCy Schubert 			net, c->str2)) {
122369c6923SCy Schubert 			log_err("out of memory while adding EDNS strings");
123c0caa2e2SCy Schubert 			return 0;
124c0caa2e2SCy Schubert 		}
125c0caa2e2SCy Schubert 	}
126369c6923SCy Schubert 	edns_strings->client_string_opcode = config->edns_client_string_opcode;
127c0caa2e2SCy Schubert 
128369c6923SCy Schubert 	addr_tree_init_parents(&edns_strings->client_strings);
129c0caa2e2SCy Schubert 	return 1;
130c0caa2e2SCy Schubert }
131c0caa2e2SCy Schubert 
132369c6923SCy Schubert struct edns_string_addr*
edns_string_addr_lookup(rbtree_type * tree,struct sockaddr_storage * addr,socklen_t addrlen)133369c6923SCy Schubert edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr,
134c0caa2e2SCy Schubert 	socklen_t addrlen)
135c0caa2e2SCy Schubert {
136369c6923SCy Schubert 	return (struct edns_string_addr*)addr_tree_lookup(tree, addr, addrlen);
137c0caa2e2SCy Schubert }
1384c75e3aaSDag-Erling Smørgrav 
1398f76bb7dSCy Schubert uint8_t*
edns_cookie_server_hash(const uint8_t * in,const uint8_t * secret,int v4,uint8_t * hash)1408f76bb7dSCy Schubert edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret, int v4,
1418f76bb7dSCy Schubert 	uint8_t* hash)
1428f76bb7dSCy Schubert {
1438f76bb7dSCy Schubert 	v4?siphash(in, 20, secret, hash, 8):siphash(in, 32, secret, hash, 8);
1448f76bb7dSCy Schubert 	return hash;
1458f76bb7dSCy Schubert }
1468f76bb7dSCy Schubert 
1478f76bb7dSCy Schubert void
edns_cookie_server_write(uint8_t * buf,const uint8_t * secret,int v4,uint32_t timestamp)1488f76bb7dSCy Schubert edns_cookie_server_write(uint8_t* buf, const uint8_t* secret, int v4,
1498f76bb7dSCy Schubert 	uint32_t timestamp)
1508f76bb7dSCy Schubert {
1518f76bb7dSCy Schubert 	uint8_t hash[8];
1528f76bb7dSCy Schubert 	buf[ 8] = 1;   /* Version */
1538f76bb7dSCy Schubert 	buf[ 9] = 0;   /* Reserved */
1548f76bb7dSCy Schubert 	buf[10] = 0;   /* Reserved */
1558f76bb7dSCy Schubert 	buf[11] = 0;   /* Reserved */
1568f76bb7dSCy Schubert 	sldns_write_uint32(buf + 12, timestamp);
1578f76bb7dSCy Schubert 	(void)edns_cookie_server_hash(buf, secret, v4, hash);
1588f76bb7dSCy Schubert 	memcpy(buf + 16, hash, 8);
1598f76bb7dSCy Schubert }
1608f76bb7dSCy Schubert 
1618f76bb7dSCy Schubert enum edns_cookie_val_status
edns_cookie_server_validate(const uint8_t * cookie,size_t cookie_len,const uint8_t * secret,size_t secret_len,int v4,const uint8_t * hash_input,uint32_t now)1628f76bb7dSCy Schubert edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len,
1638f76bb7dSCy Schubert 	const uint8_t* secret, size_t secret_len, int v4,
1648f76bb7dSCy Schubert 	const uint8_t* hash_input, uint32_t now)
1658f76bb7dSCy Schubert {
1668f76bb7dSCy Schubert 	uint8_t hash[8];
1678f76bb7dSCy Schubert 	uint32_t timestamp;
1688f76bb7dSCy Schubert 	uint32_t subt_1982 = 0; /* Initialize for the compiler; unused value */
1698f76bb7dSCy Schubert 	int comp_1982;
1708f76bb7dSCy Schubert 	if(cookie_len != 24)
1718f76bb7dSCy Schubert 		/* RFC9018 cookies are 24 bytes long */
1728f76bb7dSCy Schubert 		return COOKIE_STATUS_CLIENT_ONLY;
1738f76bb7dSCy Schubert 	if(secret_len != 16 ||  /* RFC9018 cookies have 16 byte secrets */
1748f76bb7dSCy Schubert 		cookie[8] != 1) /* RFC9018 cookies are cookie version 1 */
1758f76bb7dSCy Schubert 		return COOKIE_STATUS_INVALID;
1768f76bb7dSCy Schubert 	timestamp = sldns_read_uint32(cookie + 12);
1778f76bb7dSCy Schubert 	if((comp_1982 = compare_1982(now, timestamp)) > 0
1788f76bb7dSCy Schubert 		&& (subt_1982 = subtract_1982(timestamp, now)) > 3600)
1798f76bb7dSCy Schubert 		/* Cookie is older than 1 hour (see RFC9018 Section 4.3.) */
1808f76bb7dSCy Schubert 		return COOKIE_STATUS_EXPIRED;
1818f76bb7dSCy Schubert 	if(comp_1982 <= 0 && subtract_1982(now, timestamp) > 300)
1828f76bb7dSCy Schubert 		/* Cookie time is more than 5 minutes in the future.
1838f76bb7dSCy Schubert 		 * (see RFC9018 Section 4.3.) */
1848f76bb7dSCy Schubert 		return COOKIE_STATUS_FUTURE;
1858f76bb7dSCy Schubert 	if(memcmp(edns_cookie_server_hash(hash_input, secret, v4, hash),
1868f76bb7dSCy Schubert 		cookie + 16, 8) != 0)
1878f76bb7dSCy Schubert 		/* Hashes do not match */
1888f76bb7dSCy Schubert 		return COOKIE_STATUS_INVALID;
1898f76bb7dSCy Schubert 	if(comp_1982 > 0 && subt_1982 > 1800)
1908f76bb7dSCy Schubert 		/* Valid cookie but older than 30 minutes, so create a new one
1918f76bb7dSCy Schubert 		 * anyway */
1928f76bb7dSCy Schubert 		return COOKIE_STATUS_VALID_RENEW;
1938f76bb7dSCy Schubert 	return COOKIE_STATUS_VALID;
1948f76bb7dSCy Schubert }
195*56850988SCy Schubert 
196*56850988SCy Schubert struct cookie_secrets*
cookie_secrets_create(void)197*56850988SCy Schubert cookie_secrets_create(void)
198*56850988SCy Schubert {
199*56850988SCy Schubert 	struct cookie_secrets* cookie_secrets = calloc(1,
200*56850988SCy Schubert 		sizeof(*cookie_secrets));
201*56850988SCy Schubert 	if(!cookie_secrets)
202*56850988SCy Schubert 		return NULL;
203*56850988SCy Schubert 	lock_basic_init(&cookie_secrets->lock);
204*56850988SCy Schubert 	lock_protect(&cookie_secrets->lock, &cookie_secrets->cookie_count,
205*56850988SCy Schubert 		sizeof(cookie_secrets->cookie_count));
206*56850988SCy Schubert 	lock_protect(&cookie_secrets->lock, cookie_secrets->cookie_secrets,
207*56850988SCy Schubert 		sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
208*56850988SCy Schubert 	return cookie_secrets;
209*56850988SCy Schubert }
210*56850988SCy Schubert 
211*56850988SCy Schubert void
cookie_secrets_delete(struct cookie_secrets * cookie_secrets)212*56850988SCy Schubert cookie_secrets_delete(struct cookie_secrets* cookie_secrets)
213*56850988SCy Schubert {
214*56850988SCy Schubert 	if(!cookie_secrets)
215*56850988SCy Schubert 		return;
216*56850988SCy Schubert 	lock_basic_destroy(&cookie_secrets->lock);
217*56850988SCy Schubert 	explicit_bzero(cookie_secrets->cookie_secrets,
218*56850988SCy Schubert 		sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
219*56850988SCy Schubert 	free(cookie_secrets);
220*56850988SCy Schubert }
221*56850988SCy Schubert 
222*56850988SCy Schubert /** Read the cookie secret file */
223*56850988SCy Schubert static int
cookie_secret_file_read(struct cookie_secrets * cookie_secrets,char * cookie_secret_file)224*56850988SCy Schubert cookie_secret_file_read(struct cookie_secrets* cookie_secrets,
225*56850988SCy Schubert 	char* cookie_secret_file)
226*56850988SCy Schubert {
227*56850988SCy Schubert 	char secret[UNBOUND_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
228*56850988SCy Schubert 	FILE* f;
229*56850988SCy Schubert 	int corrupt = 0;
230*56850988SCy Schubert 	size_t count;
231*56850988SCy Schubert 
232*56850988SCy Schubert 	log_assert(cookie_secret_file != NULL);
233*56850988SCy Schubert 	cookie_secrets->cookie_count = 0;
234*56850988SCy Schubert 	f = fopen(cookie_secret_file, "r");
235*56850988SCy Schubert 	/* a non-existing cookie file is not an error */
236*56850988SCy Schubert 	if( f == NULL ) {
237*56850988SCy Schubert 		if(errno != EPERM) {
238*56850988SCy Schubert 			log_err("Could not read cookie-secret-file '%s': %s",
239*56850988SCy Schubert 				cookie_secret_file, strerror(errno));
240*56850988SCy Schubert 			return 0;
241*56850988SCy Schubert 		}
242*56850988SCy Schubert 		return 1;
243*56850988SCy Schubert 	}
244*56850988SCy Schubert 	/* cookie secret file exists and is readable */
245*56850988SCy Schubert 	for( count = 0; count < UNBOUND_COOKIE_HISTORY_SIZE; count++ ) {
246*56850988SCy Schubert 		size_t secret_len = 0;
247*56850988SCy Schubert 		ssize_t decoded_len = 0;
248*56850988SCy Schubert 		if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
249*56850988SCy Schubert 		secret_len = strlen(secret);
250*56850988SCy Schubert 		if( secret_len == 0 ) { break; }
251*56850988SCy Schubert 		log_assert( secret_len <= sizeof(secret) );
252*56850988SCy Schubert 		secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
253*56850988SCy Schubert 		if( secret_len != UNBOUND_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; }
254*56850988SCy Schubert 		/* needed for `hex_pton`; stripping potential `\n` */
255*56850988SCy Schubert 		secret[secret_len] = '\0';
256*56850988SCy Schubert 		decoded_len = hex_pton(secret, cookie_secrets->cookie_secrets[count].cookie_secret,
257*56850988SCy Schubert 		                       UNBOUND_COOKIE_SECRET_SIZE);
258*56850988SCy Schubert 		if( decoded_len != UNBOUND_COOKIE_SECRET_SIZE ) { corrupt++; break; }
259*56850988SCy Schubert 		cookie_secrets->cookie_count++;
260*56850988SCy Schubert 	}
261*56850988SCy Schubert 	fclose(f);
262*56850988SCy Schubert 	return corrupt == 0;
263*56850988SCy Schubert }
264*56850988SCy Schubert 
265*56850988SCy Schubert int
cookie_secrets_apply_cfg(struct cookie_secrets * cookie_secrets,char * cookie_secret_file)266*56850988SCy Schubert cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets,
267*56850988SCy Schubert 	char* cookie_secret_file)
268*56850988SCy Schubert {
269*56850988SCy Schubert 	if(!cookie_secrets) {
270*56850988SCy Schubert 		if(!cookie_secret_file || !cookie_secret_file[0])
271*56850988SCy Schubert 			return 1; /* There is nothing to read anyway */
272*56850988SCy Schubert 		log_err("Could not read cookie secrets, no structure alloced");
273*56850988SCy Schubert 		return 0;
274*56850988SCy Schubert 	}
275*56850988SCy Schubert 	if(!cookie_secret_file_read(cookie_secrets, cookie_secret_file))
276*56850988SCy Schubert 		return 0;
277*56850988SCy Schubert 	return 1;
278*56850988SCy Schubert }
279*56850988SCy Schubert 
280*56850988SCy Schubert enum edns_cookie_val_status
cookie_secrets_server_validate(const uint8_t * cookie,size_t cookie_len,struct cookie_secrets * cookie_secrets,int v4,const uint8_t * hash_input,uint32_t now)281*56850988SCy Schubert cookie_secrets_server_validate(const uint8_t* cookie, size_t cookie_len,
282*56850988SCy Schubert 	struct cookie_secrets* cookie_secrets, int v4,
283*56850988SCy Schubert 	const uint8_t* hash_input, uint32_t now)
284*56850988SCy Schubert {
285*56850988SCy Schubert 	size_t i;
286*56850988SCy Schubert 	enum edns_cookie_val_status cookie_val_status,
287*56850988SCy Schubert 		last = COOKIE_STATUS_INVALID;
288*56850988SCy Schubert 	if(!cookie_secrets)
289*56850988SCy Schubert 		return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
290*56850988SCy Schubert 	lock_basic_lock(&cookie_secrets->lock);
291*56850988SCy Schubert 	if(cookie_secrets->cookie_count == 0) {
292*56850988SCy Schubert 		lock_basic_unlock(&cookie_secrets->lock);
293*56850988SCy Schubert 		return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
294*56850988SCy Schubert 	}
295*56850988SCy Schubert 	for(i=0; i<cookie_secrets->cookie_count; i++) {
296*56850988SCy Schubert 		cookie_val_status = edns_cookie_server_validate(cookie,
297*56850988SCy Schubert 			cookie_len,
298*56850988SCy Schubert 			cookie_secrets->cookie_secrets[i].cookie_secret,
299*56850988SCy Schubert 			UNBOUND_COOKIE_SECRET_SIZE, v4, hash_input, now);
300*56850988SCy Schubert 		if(cookie_val_status == COOKIE_STATUS_VALID ||
301*56850988SCy Schubert 			cookie_val_status == COOKIE_STATUS_VALID_RENEW) {
302*56850988SCy Schubert 			lock_basic_unlock(&cookie_secrets->lock);
303*56850988SCy Schubert 			/* For staging cookies, write a fresh cookie. */
304*56850988SCy Schubert 			if(i != 0)
305*56850988SCy Schubert 				return COOKIE_STATUS_VALID_RENEW;
306*56850988SCy Schubert 			return cookie_val_status;
307*56850988SCy Schubert 		}
308*56850988SCy Schubert 		if(last == COOKIE_STATUS_INVALID)
309*56850988SCy Schubert 			last = cookie_val_status; /* Store more interesting
310*56850988SCy Schubert 				failure to return. */
311*56850988SCy Schubert 	}
312*56850988SCy Schubert 	lock_basic_unlock(&cookie_secrets->lock);
313*56850988SCy Schubert 	return last;
314*56850988SCy Schubert }
315*56850988SCy Schubert 
add_cookie_secret(struct cookie_secrets * cookie_secrets,uint8_t * secret,size_t secret_len)316*56850988SCy Schubert void add_cookie_secret(struct cookie_secrets* cookie_secrets,
317*56850988SCy Schubert 	uint8_t* secret, size_t secret_len)
318*56850988SCy Schubert {
319*56850988SCy Schubert 	log_assert(secret_len == UNBOUND_COOKIE_SECRET_SIZE);
320*56850988SCy Schubert 	(void)secret_len;
321*56850988SCy Schubert 	if(!cookie_secrets)
322*56850988SCy Schubert 		return;
323*56850988SCy Schubert 
324*56850988SCy Schubert 	/* New cookie secret becomes the staging secret (position 1)
325*56850988SCy Schubert 	 * unless there is no active cookie yet, then it becomes the active
326*56850988SCy Schubert 	 * secret.  If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging cookies
327*56850988SCy Schubert 	 * are moved one position down.
328*56850988SCy Schubert 	 */
329*56850988SCy Schubert 	if(cookie_secrets->cookie_count == 0) {
330*56850988SCy Schubert 		memcpy( cookie_secrets->cookie_secrets->cookie_secret
331*56850988SCy Schubert 		       , secret, UNBOUND_COOKIE_SECRET_SIZE);
332*56850988SCy Schubert 		cookie_secrets->cookie_count = 1;
333*56850988SCy Schubert 		explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
334*56850988SCy Schubert 		return;
335*56850988SCy Schubert 	}
336*56850988SCy Schubert #if UNBOUND_COOKIE_HISTORY_SIZE > 2
337*56850988SCy Schubert 	memmove( &cookie_secrets->cookie_secrets[2], &cookie_secrets->cookie_secrets[1]
338*56850988SCy Schubert 	       , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 2));
339*56850988SCy Schubert #endif
340*56850988SCy Schubert 	memcpy( cookie_secrets->cookie_secrets[1].cookie_secret
341*56850988SCy Schubert 	      , secret, UNBOUND_COOKIE_SECRET_SIZE);
342*56850988SCy Schubert 	cookie_secrets->cookie_count = cookie_secrets->cookie_count     < UNBOUND_COOKIE_HISTORY_SIZE
343*56850988SCy Schubert 	                  ? cookie_secrets->cookie_count + 1 : UNBOUND_COOKIE_HISTORY_SIZE;
344*56850988SCy Schubert 	explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
345*56850988SCy Schubert }
346*56850988SCy Schubert 
activate_cookie_secret(struct cookie_secrets * cookie_secrets)347*56850988SCy Schubert void activate_cookie_secret(struct cookie_secrets* cookie_secrets)
348*56850988SCy Schubert {
349*56850988SCy Schubert 	uint8_t active_secret[UNBOUND_COOKIE_SECRET_SIZE];
350*56850988SCy Schubert 	if(!cookie_secrets)
351*56850988SCy Schubert 		return;
352*56850988SCy Schubert 	/* The staging secret becomes the active secret.
353*56850988SCy Schubert 	 * The active secret becomes a staging secret.
354*56850988SCy Schubert 	 * If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved
355*56850988SCy Schubert 	 * one position up and the previously active secret becomes the last
356*56850988SCy Schubert 	 * staging secret.
357*56850988SCy Schubert 	 */
358*56850988SCy Schubert 	if(cookie_secrets->cookie_count < 2)
359*56850988SCy Schubert 		return;
360*56850988SCy Schubert 	memcpy( active_secret, cookie_secrets->cookie_secrets[0].cookie_secret
361*56850988SCy Schubert 	      , UNBOUND_COOKIE_SECRET_SIZE);
362*56850988SCy Schubert 	memmove( &cookie_secrets->cookie_secrets[0], &cookie_secrets->cookie_secrets[1]
363*56850988SCy Schubert 	       , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 1));
364*56850988SCy Schubert 	memcpy( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
365*56850988SCy Schubert 	      , active_secret, UNBOUND_COOKIE_SECRET_SIZE);
366*56850988SCy Schubert 	explicit_bzero(active_secret, UNBOUND_COOKIE_SECRET_SIZE);
367*56850988SCy Schubert }
368*56850988SCy Schubert 
drop_cookie_secret(struct cookie_secrets * cookie_secrets)369*56850988SCy Schubert void drop_cookie_secret(struct cookie_secrets* cookie_secrets)
370*56850988SCy Schubert {
371*56850988SCy Schubert 	if(!cookie_secrets)
372*56850988SCy Schubert 		return;
373*56850988SCy Schubert 	/* Drops a staging cookie secret. If there are more than one, it will
374*56850988SCy Schubert 	 * drop the last staging secret. */
375*56850988SCy Schubert 	if(cookie_secrets->cookie_count < 2)
376*56850988SCy Schubert 		return;
377*56850988SCy Schubert 	explicit_bzero( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
378*56850988SCy Schubert 	              , UNBOUND_COOKIE_SECRET_SIZE);
379*56850988SCy Schubert 	cookie_secrets->cookie_count -= 1;
380*56850988SCy Schubert }
381