mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-02-18 18:25:10 -05:00
- xfr-tsig, tsig_verify_reply function.
This commit is contained in:
parent
57dd6a971d
commit
0955238cd3
2 changed files with 176 additions and 5 deletions
163
util/tsig.c
163
util/tsig.c
|
|
@ -553,14 +553,16 @@ tsig_algo_calc(struct tsig_key* key, struct sldns_buffer* pkt,
|
|||
* Called must hold locks, on key. The routine concatenates the argument
|
||||
* datas with digest updates.
|
||||
* @param key: the key, and algorithm.
|
||||
* @param prior: before the next buffer. or NULL.
|
||||
* @param pkt: packet with contents.
|
||||
* @param var: variables to digest.
|
||||
* @param tsig: where to store the result.
|
||||
* @return false on failure.
|
||||
*/
|
||||
static int
|
||||
tsig_algo_calc_parts(struct tsig_key* key, struct sldns_buffer* pkt,
|
||||
struct sldns_buffer* var, struct tsig_data* tsig)
|
||||
tsig_algo_calc_parts(struct tsig_key* key, struct sldns_buffer* prior,
|
||||
struct sldns_buffer* pkt, struct sldns_buffer* var,
|
||||
struct tsig_data* tsig)
|
||||
{
|
||||
#ifdef HAVE_EVP_MAC_CTX_NEW
|
||||
/* For EVP_MAC_CTX_new and OpenSSL since 3.0 functions. */
|
||||
|
|
@ -582,6 +584,15 @@ tsig_algo_calc_parts(struct tsig_key* key, struct sldns_buffer* pkt,
|
|||
EVP_MAC_free(mac);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(prior && !EVP_MAC_update(ctx, sldns_buffer_begin(prior),
|
||||
sldns_buffer_position(prior))) {
|
||||
log_crypto_err("Could not EVP_MAC_update");
|
||||
EVP_MAC_CTX_free(ctx);
|
||||
EVP_MAC_free(mac);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!EVP_MAC_update(ctx, sldns_buffer_begin(pkt),
|
||||
sldns_buffer_position(pkt))) {
|
||||
log_crypto_err("Could not EVP_MAC_update");
|
||||
|
|
@ -635,6 +646,12 @@ tsig_algo_calc_parts(struct tsig_key* key, struct sldns_buffer* pkt,
|
|||
HMAC_CTX_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
if(prior && !HMAC_Update(ctx, sldns_buffer_begin(prior),
|
||||
sldns_buffer_position(prior))) {
|
||||
log_crypto_err("Could not HMAC_Update");
|
||||
HMAC_CTX_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
if(!HMAC_Update(ctx, sldns_buffer_begin(pkt),
|
||||
sldns_buffer_position(pkt))) {
|
||||
log_crypto_err("Could not HMAC_Update");
|
||||
|
|
@ -685,6 +702,20 @@ tsig_algo_calc_parts(struct tsig_key* key, struct sldns_buffer* pkt,
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
if(prior) {
|
||||
#ifndef HMAC_INIT_EX_RETURNS_VOID
|
||||
r =
|
||||
#endif
|
||||
HMAC_Update(&ctx, sldns_buffer_begin(prior),
|
||||
sldns_buffer_position(prior));
|
||||
#ifndef HMAC_INIT_EX_RETURNS_VOID
|
||||
if(!r) {
|
||||
log_crypto_err("Could not HMAC_Update");
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifndef HMAC_INIT_EX_RETURNS_VOID
|
||||
r =
|
||||
#endif
|
||||
|
|
@ -914,7 +945,7 @@ tsig_verify_query(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
|||
|
||||
tsig_write_vars_parsed(rr, &var, key);
|
||||
|
||||
if(!tsig_algo_calc_parts(key, pkt, &var, tsig)) {
|
||||
if(!tsig_algo_calc_parts(key, NULL, pkt, &var, tsig)) {
|
||||
/* Failure to calculate digest. */
|
||||
sldns_buffer_write_u16_at(pkt, 0, current_query_id);
|
||||
verbose(VERB_ALGO, "tsig_verify_query: failed to calculate digest");
|
||||
|
|
@ -1414,7 +1445,7 @@ tsig_sign_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
|||
sldns_buffer_write(&var, tsig->mac, tsig->mac_size);
|
||||
|
||||
/* Calculate the TSIG. */
|
||||
if(!tsig_algo_calc_parts(key, &var, pkt, tsig)) {
|
||||
if(!tsig_algo_calc_parts(key, NULL, &var, pkt, tsig)) {
|
||||
/* Failure to calculate digest. */
|
||||
lock_rw_unlock(&key_table->lock);
|
||||
sldns_buffer_write_u16_at(pkt, 0, current_query_id);
|
||||
|
|
@ -1430,6 +1461,130 @@ tsig_sign_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
|||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
tsig_verify_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
||||
struct tsig_key* key, struct tsig_record* rr, uint64_t now)
|
||||
{
|
||||
uint8_t var_buf[2048], prior_buf[1024];
|
||||
struct sldns_buffer var, prior;
|
||||
uint16_t current_query_id;
|
||||
|
||||
sldns_buffer_init_frm_data(&var, var_buf, sizeof(var_buf));
|
||||
sldns_buffer_init_frm_data(&prior, prior_buf, sizeof(prior_buf));
|
||||
|
||||
/* Create the variables to digest in the buffer. */
|
||||
/* packet with original query ID and ARCOUNT without TSIG. */
|
||||
/* It is prepended with the prior mac. */
|
||||
if(!tsig_vars_available_parsed(rr, &var)) {
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: variable buffer too small");
|
||||
return 0;
|
||||
}
|
||||
if(sldns_buffer_remaining(&prior) <
|
||||
2 /* mac_size */ + tsig->mac_size) {
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: prior buffer too small");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sldns_buffer_write_u16(&prior, tsig->mac_size);
|
||||
sldns_buffer_write(&prior, tsig->mac, tsig->mac_size);
|
||||
|
||||
current_query_id = sldns_buffer_read_u16_at(pkt, 0);
|
||||
sldns_buffer_write_u16_at(pkt, 0, rr->original_query_id);
|
||||
LDNS_ARCOUNT_SET( sldns_buffer_begin(pkt)
|
||||
, LDNS_ARCOUNT(sldns_buffer_begin(pkt)) - 1);
|
||||
sldns_buffer_set_position(pkt, rr->tsig_pos);
|
||||
|
||||
tsig_write_vars_parsed(rr, &var, key);
|
||||
|
||||
if(!tsig_algo_calc_parts(key, &prior, pkt, &var, tsig)) {
|
||||
/* Failure to calculate digest. */
|
||||
sldns_buffer_write_u16_at(pkt, 0, current_query_id);
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: failed to calculate digest");
|
||||
return 0;
|
||||
}
|
||||
if(CRYPTO_memcmp(rr->mac_data, tsig->mac, tsig->mac_size) != 0) {
|
||||
/* TSIG has wrong digest. */
|
||||
sldns_buffer_write_u16_at(pkt, 0, current_query_id);
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
char keynm[256];
|
||||
dname_str(tsig->key_name, keynm);
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: TSIG %s has wrong digest",
|
||||
keynm);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* The TSIG digest has verified */
|
||||
sldns_buffer_write_u16_at(pkt, 0, current_query_id);
|
||||
|
||||
/* Check the TSIG timestamp. */
|
||||
/* Time is checked after the MAC is verified */
|
||||
if( (now > rr->signed_time && now - rr->signed_time > rr->fudge_time) ||
|
||||
(now < rr->signed_time && rr->signed_time - now > rr->fudge_time)) {
|
||||
/* TSIG has wrong timestamp. */
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
char keynm[256];
|
||||
dname_str(tsig->key_name, keynm);
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: TSIG %s has wrong timestamp, now=%llu packet time=%llu fudge time=%d",
|
||||
keynm, (unsigned long long)now,
|
||||
(unsigned long long)rr->signed_time,
|
||||
(int)rr->fudge_time);
|
||||
if(rr->other_size == 6)
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: other time is reported at %llu",
|
||||
(unsigned long long)rr->other_time);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
tsig_parse_verify_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
||||
struct tsig_key_table* key_table, uint64_t now)
|
||||
{
|
||||
struct tsig_key* key;
|
||||
struct tsig_record rr;
|
||||
int ret;
|
||||
|
||||
/* Parse the TSIG RR from the query. */
|
||||
ret = tsig_parse(pkt, &rr);
|
||||
if(ret != 0)
|
||||
return 0;
|
||||
|
||||
/* Check it is the same key, same algo, same digest_size */
|
||||
if(dname_pkt_compare(pkt, tsig->key_name, rr.key_name) != 0) {
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: key name wrong");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_rw_rdlock(&key_table->lock);
|
||||
key = tsig_key_table_search(key_table, tsig->key_name,
|
||||
tsig->key_name_len);
|
||||
if(!key) {
|
||||
/* The tsig key has disappeared from the key table. */
|
||||
lock_rw_unlock(&key_table->lock);
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: key not in table");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(query_dname_compare(key->algo->wireformat_name,
|
||||
rr.algorithm_name) != 0) {
|
||||
lock_rw_unlock(&key_table->lock);
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: algorithm wrong");
|
||||
return 0;
|
||||
}
|
||||
if(tsig->mac_size != rr.mac_size) {
|
||||
lock_rw_unlock(&key_table->lock);
|
||||
verbose(VERB_ALGO, "tsig_verify_reply: mac size wrong");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify the TSIG. */
|
||||
ret = tsig_verify_reply(tsig, pkt, key, &rr, now);
|
||||
lock_rw_unlock(&key_table->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify pkt with the name (domain name), algorithm and key in Base64.
|
||||
* out 0 on success, an error code otherwise.
|
||||
|
|
|
|||
18
util/tsig.h
18
util/tsig.h
|
|
@ -381,10 +381,26 @@ int tsig_sign_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
|||
* Verify a reply with TSIG.
|
||||
* @param tsig: the tsig data.
|
||||
* @param pkt: the reply to verify.
|
||||
* @param key: the key with algorithm, caller must hold lock.
|
||||
* @param rr: the tsig record parsed from the reply.
|
||||
* @param now: time to sign the query, the current time.
|
||||
* @return false on failure, like
|
||||
* alloc failure, wireformat malformed, did not verify.
|
||||
*/
|
||||
int tsig_verify_reply(struct tsig_data* tsig, struct sldns_buffer* pkt);
|
||||
int tsig_verify_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
||||
struct tsig_key* key, struct tsig_record* rr, uint64_t now);
|
||||
|
||||
/**
|
||||
* Verify a reply with TSIG.
|
||||
* @param tsig: the tsig data.
|
||||
* @param pkt: the reply to verify.
|
||||
* @param key_table: the tsig key table is used to fetch the key details.
|
||||
* @param now: time to sign the query, the current time.
|
||||
* @return false on failure, like
|
||||
* alloc failure, wireformat malformed, did not verify.
|
||||
*/
|
||||
int tsig_parse_verify_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
|
||||
struct tsig_key_table* key_table, uint64_t now);
|
||||
|
||||
/**
|
||||
* Calculate reserved space for TSIG.
|
||||
|
|
|
|||
Loading…
Reference in a new issue