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