xref: /openbsd-src/usr.sbin/unbound/util/edns.c (revision a43524d9cc222a049058246319ec6a29f2d9ca78)
17bc20e6dSsthen /*
27bc20e6dSsthen  * util/edns.c - handle base EDNS options.
37bc20e6dSsthen  *
47bc20e6dSsthen  * Copyright (c) 2018, NLnet Labs. All rights reserved.
57bc20e6dSsthen  *
67bc20e6dSsthen  * This software is open source.
77bc20e6dSsthen  *
87bc20e6dSsthen  * Redistribution and use in source and binary forms, with or without
97bc20e6dSsthen  * modification, are permitted provided that the following conditions
107bc20e6dSsthen  * are met:
117bc20e6dSsthen  *
127bc20e6dSsthen  * Redistributions of source code must retain the above copyright notice,
137bc20e6dSsthen  * this list of conditions and the following disclaimer.
147bc20e6dSsthen  *
157bc20e6dSsthen  * Redistributions in binary form must reproduce the above copyright notice,
167bc20e6dSsthen  * this list of conditions and the following disclaimer in the documentation
177bc20e6dSsthen  * and/or other materials provided with the distribution.
187bc20e6dSsthen  *
197bc20e6dSsthen  * Neither the name of the NLNET LABS nor the names of its contributors may
207bc20e6dSsthen  * be used to endorse or promote products derived from this software without
217bc20e6dSsthen  * specific prior written permission.
227bc20e6dSsthen  *
237bc20e6dSsthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
247bc20e6dSsthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
257bc20e6dSsthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
267bc20e6dSsthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
277bc20e6dSsthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
287bc20e6dSsthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
297bc20e6dSsthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
307bc20e6dSsthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
317bc20e6dSsthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
327bc20e6dSsthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
337bc20e6dSsthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
347bc20e6dSsthen  */
357bc20e6dSsthen 
367bc20e6dSsthen /**
377bc20e6dSsthen  * \file
387bc20e6dSsthen  *
397bc20e6dSsthen  * This file contains functions for base EDNS options.
407bc20e6dSsthen  */
417bc20e6dSsthen 
427bc20e6dSsthen #include "config.h"
4366a34dc2Ssthen #include "util/edns.h"
447bc20e6dSsthen #include "util/config_file.h"
457bc20e6dSsthen #include "util/netevent.h"
46e2a0f313Ssthen #include "util/net_help.h"
477bc20e6dSsthen #include "util/regional.h"
48437d2860Ssthen #include "util/rfc_1982.h"
49437d2860Ssthen #include "util/siphash.h"
507bc20e6dSsthen #include "util/data/msgparse.h"
517bc20e6dSsthen #include "util/data/msgreply.h"
52437d2860Ssthen #include "sldns/sbuffer.h"
537bc20e6dSsthen 
5472f58708Ssthen struct edns_strings* edns_strings_create(void)
55e2a0f313Ssthen {
5672f58708Ssthen 	struct edns_strings* edns_strings = calloc(1,
5772f58708Ssthen 		sizeof(struct edns_strings));
5872f58708Ssthen 	if(!edns_strings)
59e2a0f313Ssthen 		return NULL;
6072f58708Ssthen 	if(!(edns_strings->region = regional_create())) {
6172f58708Ssthen 		edns_strings_delete(edns_strings);
62e2a0f313Ssthen 		return NULL;
63e2a0f313Ssthen 	}
6472f58708Ssthen 	return edns_strings;
65e2a0f313Ssthen }
66e2a0f313Ssthen 
6772f58708Ssthen void edns_strings_delete(struct edns_strings* edns_strings)
68e2a0f313Ssthen {
6972f58708Ssthen 	if(!edns_strings)
70e2a0f313Ssthen 		return;
7172f58708Ssthen 	regional_destroy(edns_strings->region);
7272f58708Ssthen 	free(edns_strings);
73e2a0f313Ssthen }
74e2a0f313Ssthen 
75e2a0f313Ssthen static int
7672f58708Ssthen edns_strings_client_insert(struct edns_strings* edns_strings,
77e2a0f313Ssthen 	struct sockaddr_storage* addr, socklen_t addrlen, int net,
7872f58708Ssthen 	const char* string)
79e2a0f313Ssthen {
8072f58708Ssthen 	struct edns_string_addr* esa = regional_alloc_zero(edns_strings->region,
8172f58708Ssthen 		sizeof(struct edns_string_addr));
8272f58708Ssthen 	if(!esa)
83e2a0f313Ssthen 		return 0;
8472f58708Ssthen 	esa->string_len = strlen(string);
8572f58708Ssthen 	esa->string = regional_alloc_init(edns_strings->region, string,
8672f58708Ssthen 		esa->string_len);
8772f58708Ssthen 	if(!esa->string)
8872f58708Ssthen 		return 0;
8972f58708Ssthen 	if(!addr_tree_insert(&edns_strings->client_strings, &esa->node, addr,
9072f58708Ssthen 		addrlen, net)) {
9172f58708Ssthen 		verbose(VERB_QUERY, "duplicate EDNS client string ignored.");
92e2a0f313Ssthen 	}
93e2a0f313Ssthen 	return 1;
94e2a0f313Ssthen }
95e2a0f313Ssthen 
9672f58708Ssthen int edns_strings_apply_cfg(struct edns_strings* edns_strings,
97e2a0f313Ssthen 	struct config_file* config)
98e2a0f313Ssthen {
99e2a0f313Ssthen 	struct config_str2list* c;
10072f58708Ssthen 	regional_free_all(edns_strings->region);
10172f58708Ssthen 	addr_tree_init(&edns_strings->client_strings);
102e2a0f313Ssthen 
10372f58708Ssthen 	for(c=config->edns_client_strings; c; c=c->next) {
104e2a0f313Ssthen 		struct sockaddr_storage addr;
105e2a0f313Ssthen 		socklen_t addrlen;
106e2a0f313Ssthen 		int net;
107e2a0f313Ssthen 		log_assert(c->str && c->str2);
108e2a0f313Ssthen 
109e2a0f313Ssthen 		if(!netblockstrtoaddr(c->str, UNBOUND_DNS_PORT, &addr, &addrlen,
110e2a0f313Ssthen 			&net)) {
11172f58708Ssthen 			log_err("cannot parse EDNS client string IP netblock: "
11272f58708Ssthen 				"%s", c->str);
113e2a0f313Ssthen 			return 0;
114e2a0f313Ssthen 		}
11572f58708Ssthen 		if(!edns_strings_client_insert(edns_strings, &addr, addrlen,
11672f58708Ssthen 			net, c->str2)) {
11772f58708Ssthen 			log_err("out of memory while adding EDNS strings");
118e2a0f313Ssthen 			return 0;
119e2a0f313Ssthen 		}
120e2a0f313Ssthen 	}
12172f58708Ssthen 	edns_strings->client_string_opcode = config->edns_client_string_opcode;
122e2a0f313Ssthen 
12372f58708Ssthen 	addr_tree_init_parents(&edns_strings->client_strings);
124e2a0f313Ssthen 	return 1;
125e2a0f313Ssthen }
126e2a0f313Ssthen 
12772f58708Ssthen struct edns_string_addr*
12872f58708Ssthen edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr,
129e2a0f313Ssthen 	socklen_t addrlen)
130e2a0f313Ssthen {
13172f58708Ssthen 	return (struct edns_string_addr*)addr_tree_lookup(tree, addr, addrlen);
132e2a0f313Ssthen }
133e2a0f313Ssthen 
134437d2860Ssthen uint8_t*
135437d2860Ssthen edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret, int v4,
136437d2860Ssthen 	uint8_t* hash)
137437d2860Ssthen {
138437d2860Ssthen 	v4?siphash(in, 20, secret, hash, 8):siphash(in, 32, secret, hash, 8);
139437d2860Ssthen 	return hash;
140437d2860Ssthen }
141437d2860Ssthen 
142437d2860Ssthen void
143437d2860Ssthen edns_cookie_server_write(uint8_t* buf, const uint8_t* secret, int v4,
144437d2860Ssthen 	uint32_t timestamp)
145437d2860Ssthen {
146437d2860Ssthen 	uint8_t hash[8];
147437d2860Ssthen 	buf[ 8] = 1;   /* Version */
148437d2860Ssthen 	buf[ 9] = 0;   /* Reserved */
149437d2860Ssthen 	buf[10] = 0;   /* Reserved */
150437d2860Ssthen 	buf[11] = 0;   /* Reserved */
151437d2860Ssthen 	sldns_write_uint32(buf + 12, timestamp);
152437d2860Ssthen 	(void)edns_cookie_server_hash(buf, secret, v4, hash);
153437d2860Ssthen 	memcpy(buf + 16, hash, 8);
154437d2860Ssthen }
155437d2860Ssthen 
156437d2860Ssthen enum edns_cookie_val_status
157437d2860Ssthen edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len,
158437d2860Ssthen 	const uint8_t* secret, size_t secret_len, int v4,
159437d2860Ssthen 	const uint8_t* hash_input, uint32_t now)
160437d2860Ssthen {
161437d2860Ssthen 	uint8_t hash[8];
162437d2860Ssthen 	uint32_t timestamp;
163437d2860Ssthen 	uint32_t subt_1982 = 0; /* Initialize for the compiler; unused value */
164437d2860Ssthen 	int comp_1982;
165437d2860Ssthen 	if(cookie_len != 24)
166437d2860Ssthen 		/* RFC9018 cookies are 24 bytes long */
167437d2860Ssthen 		return COOKIE_STATUS_CLIENT_ONLY;
168437d2860Ssthen 	if(secret_len != 16 ||  /* RFC9018 cookies have 16 byte secrets */
169437d2860Ssthen 		cookie[8] != 1) /* RFC9018 cookies are cookie version 1 */
170437d2860Ssthen 		return COOKIE_STATUS_INVALID;
171437d2860Ssthen 	timestamp = sldns_read_uint32(cookie + 12);
172437d2860Ssthen 	if((comp_1982 = compare_1982(now, timestamp)) > 0
173437d2860Ssthen 		&& (subt_1982 = subtract_1982(timestamp, now)) > 3600)
174437d2860Ssthen 		/* Cookie is older than 1 hour (see RFC9018 Section 4.3.) */
175437d2860Ssthen 		return COOKIE_STATUS_EXPIRED;
176437d2860Ssthen 	if(comp_1982 <= 0 && subtract_1982(now, timestamp) > 300)
177437d2860Ssthen 		/* Cookie time is more than 5 minutes in the future.
178437d2860Ssthen 		 * (see RFC9018 Section 4.3.) */
179437d2860Ssthen 		return COOKIE_STATUS_FUTURE;
180437d2860Ssthen 	if(memcmp(edns_cookie_server_hash(hash_input, secret, v4, hash),
181437d2860Ssthen 		cookie + 16, 8) != 0)
182437d2860Ssthen 		/* Hashes do not match */
183437d2860Ssthen 		return COOKIE_STATUS_INVALID;
184437d2860Ssthen 	if(comp_1982 > 0 && subt_1982 > 1800)
185437d2860Ssthen 		/* Valid cookie but older than 30 minutes, so create a new one
186437d2860Ssthen 		 * anyway */
187437d2860Ssthen 		return COOKIE_STATUS_VALID_RENEW;
188437d2860Ssthen 	return COOKIE_STATUS_VALID;
189437d2860Ssthen }
190*a43524d9Ssthen 
191*a43524d9Ssthen struct cookie_secrets*
192*a43524d9Ssthen cookie_secrets_create(void)
193*a43524d9Ssthen {
194*a43524d9Ssthen 	struct cookie_secrets* cookie_secrets = calloc(1,
195*a43524d9Ssthen 		sizeof(*cookie_secrets));
196*a43524d9Ssthen 	if(!cookie_secrets)
197*a43524d9Ssthen 		return NULL;
198*a43524d9Ssthen 	lock_basic_init(&cookie_secrets->lock);
199*a43524d9Ssthen 	lock_protect(&cookie_secrets->lock, &cookie_secrets->cookie_count,
200*a43524d9Ssthen 		sizeof(cookie_secrets->cookie_count));
201*a43524d9Ssthen 	lock_protect(&cookie_secrets->lock, cookie_secrets->cookie_secrets,
202*a43524d9Ssthen 		sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
203*a43524d9Ssthen 	return cookie_secrets;
204*a43524d9Ssthen }
205*a43524d9Ssthen 
206*a43524d9Ssthen void
207*a43524d9Ssthen cookie_secrets_delete(struct cookie_secrets* cookie_secrets)
208*a43524d9Ssthen {
209*a43524d9Ssthen 	if(!cookie_secrets)
210*a43524d9Ssthen 		return;
211*a43524d9Ssthen 	lock_basic_destroy(&cookie_secrets->lock);
212*a43524d9Ssthen 	explicit_bzero(cookie_secrets->cookie_secrets,
213*a43524d9Ssthen 		sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
214*a43524d9Ssthen 	free(cookie_secrets);
215*a43524d9Ssthen }
216*a43524d9Ssthen 
217*a43524d9Ssthen /** Read the cookie secret file */
218*a43524d9Ssthen static int
219*a43524d9Ssthen cookie_secret_file_read(struct cookie_secrets* cookie_secrets,
220*a43524d9Ssthen 	char* cookie_secret_file)
221*a43524d9Ssthen {
222*a43524d9Ssthen 	char secret[UNBOUND_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
223*a43524d9Ssthen 	FILE* f;
224*a43524d9Ssthen 	int corrupt = 0;
225*a43524d9Ssthen 	size_t count;
226*a43524d9Ssthen 
227*a43524d9Ssthen 	log_assert(cookie_secret_file != NULL);
228*a43524d9Ssthen 	cookie_secrets->cookie_count = 0;
229*a43524d9Ssthen 	f = fopen(cookie_secret_file, "r");
230*a43524d9Ssthen 	/* a non-existing cookie file is not an error */
231*a43524d9Ssthen 	if( f == NULL ) {
232*a43524d9Ssthen 		if(errno != EPERM) {
233*a43524d9Ssthen 			log_err("Could not read cookie-secret-file '%s': %s",
234*a43524d9Ssthen 				cookie_secret_file, strerror(errno));
235*a43524d9Ssthen 			return 0;
236*a43524d9Ssthen 		}
237*a43524d9Ssthen 		return 1;
238*a43524d9Ssthen 	}
239*a43524d9Ssthen 	/* cookie secret file exists and is readable */
240*a43524d9Ssthen 	for( count = 0; count < UNBOUND_COOKIE_HISTORY_SIZE; count++ ) {
241*a43524d9Ssthen 		size_t secret_len = 0;
242*a43524d9Ssthen 		ssize_t decoded_len = 0;
243*a43524d9Ssthen 		if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
244*a43524d9Ssthen 		secret_len = strlen(secret);
245*a43524d9Ssthen 		if( secret_len == 0 ) { break; }
246*a43524d9Ssthen 		log_assert( secret_len <= sizeof(secret) );
247*a43524d9Ssthen 		secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
248*a43524d9Ssthen 		if( secret_len != UNBOUND_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; }
249*a43524d9Ssthen 		/* needed for `hex_pton`; stripping potential `\n` */
250*a43524d9Ssthen 		secret[secret_len] = '\0';
251*a43524d9Ssthen 		decoded_len = hex_pton(secret, cookie_secrets->cookie_secrets[count].cookie_secret,
252*a43524d9Ssthen 		                       UNBOUND_COOKIE_SECRET_SIZE);
253*a43524d9Ssthen 		if( decoded_len != UNBOUND_COOKIE_SECRET_SIZE ) { corrupt++; break; }
254*a43524d9Ssthen 		cookie_secrets->cookie_count++;
255*a43524d9Ssthen 	}
256*a43524d9Ssthen 	fclose(f);
257*a43524d9Ssthen 	return corrupt == 0;
258*a43524d9Ssthen }
259*a43524d9Ssthen 
260*a43524d9Ssthen int
261*a43524d9Ssthen cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets,
262*a43524d9Ssthen 	char* cookie_secret_file)
263*a43524d9Ssthen {
264*a43524d9Ssthen 	if(!cookie_secrets) {
265*a43524d9Ssthen 		if(!cookie_secret_file || !cookie_secret_file[0])
266*a43524d9Ssthen 			return 1; /* There is nothing to read anyway */
267*a43524d9Ssthen 		log_err("Could not read cookie secrets, no structure alloced");
268*a43524d9Ssthen 		return 0;
269*a43524d9Ssthen 	}
270*a43524d9Ssthen 	if(!cookie_secret_file_read(cookie_secrets, cookie_secret_file))
271*a43524d9Ssthen 		return 0;
272*a43524d9Ssthen 	return 1;
273*a43524d9Ssthen }
274*a43524d9Ssthen 
275*a43524d9Ssthen enum edns_cookie_val_status
276*a43524d9Ssthen cookie_secrets_server_validate(const uint8_t* cookie, size_t cookie_len,
277*a43524d9Ssthen 	struct cookie_secrets* cookie_secrets, int v4,
278*a43524d9Ssthen 	const uint8_t* hash_input, uint32_t now)
279*a43524d9Ssthen {
280*a43524d9Ssthen 	size_t i;
281*a43524d9Ssthen 	enum edns_cookie_val_status cookie_val_status,
282*a43524d9Ssthen 		last = COOKIE_STATUS_INVALID;
283*a43524d9Ssthen 	if(!cookie_secrets)
284*a43524d9Ssthen 		return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
285*a43524d9Ssthen 	lock_basic_lock(&cookie_secrets->lock);
286*a43524d9Ssthen 	if(cookie_secrets->cookie_count == 0) {
287*a43524d9Ssthen 		lock_basic_unlock(&cookie_secrets->lock);
288*a43524d9Ssthen 		return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
289*a43524d9Ssthen 	}
290*a43524d9Ssthen 	for(i=0; i<cookie_secrets->cookie_count; i++) {
291*a43524d9Ssthen 		cookie_val_status = edns_cookie_server_validate(cookie,
292*a43524d9Ssthen 			cookie_len,
293*a43524d9Ssthen 			cookie_secrets->cookie_secrets[i].cookie_secret,
294*a43524d9Ssthen 			UNBOUND_COOKIE_SECRET_SIZE, v4, hash_input, now);
295*a43524d9Ssthen 		if(cookie_val_status == COOKIE_STATUS_VALID ||
296*a43524d9Ssthen 			cookie_val_status == COOKIE_STATUS_VALID_RENEW) {
297*a43524d9Ssthen 			lock_basic_unlock(&cookie_secrets->lock);
298*a43524d9Ssthen 			/* For staging cookies, write a fresh cookie. */
299*a43524d9Ssthen 			if(i != 0)
300*a43524d9Ssthen 				return COOKIE_STATUS_VALID_RENEW;
301*a43524d9Ssthen 			return cookie_val_status;
302*a43524d9Ssthen 		}
303*a43524d9Ssthen 		if(last == COOKIE_STATUS_INVALID)
304*a43524d9Ssthen 			last = cookie_val_status; /* Store more interesting
305*a43524d9Ssthen 				failure to return. */
306*a43524d9Ssthen 	}
307*a43524d9Ssthen 	lock_basic_unlock(&cookie_secrets->lock);
308*a43524d9Ssthen 	return last;
309*a43524d9Ssthen }
310*a43524d9Ssthen 
311*a43524d9Ssthen void add_cookie_secret(struct cookie_secrets* cookie_secrets,
312*a43524d9Ssthen 	uint8_t* secret, size_t secret_len)
313*a43524d9Ssthen {
314*a43524d9Ssthen 	log_assert(secret_len == UNBOUND_COOKIE_SECRET_SIZE);
315*a43524d9Ssthen 	(void)secret_len;
316*a43524d9Ssthen 	if(!cookie_secrets)
317*a43524d9Ssthen 		return;
318*a43524d9Ssthen 
319*a43524d9Ssthen 	/* New cookie secret becomes the staging secret (position 1)
320*a43524d9Ssthen 	 * unless there is no active cookie yet, then it becomes the active
321*a43524d9Ssthen 	 * secret.  If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging cookies
322*a43524d9Ssthen 	 * are moved one position down.
323*a43524d9Ssthen 	 */
324*a43524d9Ssthen 	if(cookie_secrets->cookie_count == 0) {
325*a43524d9Ssthen 		memcpy( cookie_secrets->cookie_secrets->cookie_secret
326*a43524d9Ssthen 		       , secret, UNBOUND_COOKIE_SECRET_SIZE);
327*a43524d9Ssthen 		cookie_secrets->cookie_count = 1;
328*a43524d9Ssthen 		explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
329*a43524d9Ssthen 		return;
330*a43524d9Ssthen 	}
331*a43524d9Ssthen #if UNBOUND_COOKIE_HISTORY_SIZE > 2
332*a43524d9Ssthen 	memmove( &cookie_secrets->cookie_secrets[2], &cookie_secrets->cookie_secrets[1]
333*a43524d9Ssthen 	       , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 2));
334*a43524d9Ssthen #endif
335*a43524d9Ssthen 	memcpy( cookie_secrets->cookie_secrets[1].cookie_secret
336*a43524d9Ssthen 	      , secret, UNBOUND_COOKIE_SECRET_SIZE);
337*a43524d9Ssthen 	cookie_secrets->cookie_count = cookie_secrets->cookie_count     < UNBOUND_COOKIE_HISTORY_SIZE
338*a43524d9Ssthen 	                  ? cookie_secrets->cookie_count + 1 : UNBOUND_COOKIE_HISTORY_SIZE;
339*a43524d9Ssthen 	explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
340*a43524d9Ssthen }
341*a43524d9Ssthen 
342*a43524d9Ssthen void activate_cookie_secret(struct cookie_secrets* cookie_secrets)
343*a43524d9Ssthen {
344*a43524d9Ssthen 	uint8_t active_secret[UNBOUND_COOKIE_SECRET_SIZE];
345*a43524d9Ssthen 	if(!cookie_secrets)
346*a43524d9Ssthen 		return;
347*a43524d9Ssthen 	/* The staging secret becomes the active secret.
348*a43524d9Ssthen 	 * The active secret becomes a staging secret.
349*a43524d9Ssthen 	 * If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved
350*a43524d9Ssthen 	 * one position up and the previously active secret becomes the last
351*a43524d9Ssthen 	 * staging secret.
352*a43524d9Ssthen 	 */
353*a43524d9Ssthen 	if(cookie_secrets->cookie_count < 2)
354*a43524d9Ssthen 		return;
355*a43524d9Ssthen 	memcpy( active_secret, cookie_secrets->cookie_secrets[0].cookie_secret
356*a43524d9Ssthen 	      , UNBOUND_COOKIE_SECRET_SIZE);
357*a43524d9Ssthen 	memmove( &cookie_secrets->cookie_secrets[0], &cookie_secrets->cookie_secrets[1]
358*a43524d9Ssthen 	       , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 1));
359*a43524d9Ssthen 	memcpy( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
360*a43524d9Ssthen 	      , active_secret, UNBOUND_COOKIE_SECRET_SIZE);
361*a43524d9Ssthen 	explicit_bzero(active_secret, UNBOUND_COOKIE_SECRET_SIZE);
362*a43524d9Ssthen }
363*a43524d9Ssthen 
364*a43524d9Ssthen void drop_cookie_secret(struct cookie_secrets* cookie_secrets)
365*a43524d9Ssthen {
366*a43524d9Ssthen 	if(!cookie_secrets)
367*a43524d9Ssthen 		return;
368*a43524d9Ssthen 	/* Drops a staging cookie secret. If there are more than one, it will
369*a43524d9Ssthen 	 * drop the last staging secret. */
370*a43524d9Ssthen 	if(cookie_secrets->cookie_count < 2)
371*a43524d9Ssthen 		return;
372*a43524d9Ssthen 	explicit_bzero( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
373*a43524d9Ssthen 	              , UNBOUND_COOKIE_SECRET_SIZE);
374*a43524d9Ssthen 	cookie_secrets->cookie_count -= 1;
375*a43524d9Ssthen }
376