_public_ int knot_rdataset_sort_at(knot_rdataset_t *rrs, size_t pos, knot_mm_t *mm) { if (rrs == NULL || rrs->rr_count == 0) { return KNOT_EINVAL; } knot_rdata_t *rr = knot_rdataset_at(rrs, pos); assert(rr); knot_rdata_t *earlier_rr = NULL; for (uint16_t i = 0; i < rrs->rr_count; ++i) { if (i == pos) { // It already is at the position return KNOT_EOK; } earlier_rr = knot_rdataset_at(rrs, i); int cmp = knot_rdata_cmp(earlier_rr, rr); if (cmp == 0) { // Duplication - we need to remove this RR return remove_rr_at(rrs, pos, mm); } else if (cmp > 0) { // Found position to move break; } } // RDATA have to be rearanged. knot_rdata_t *last_rr = knot_rdataset_at(rrs, pos - 1); assert(last_rr); assert(earlier_rr); // Save the RR to be moved const uint16_t size = knot_rdata_rdlen(rr); const uint32_t ttl = knot_rdata_ttl(rr); const uint8_t *rdata = knot_rdata_data(rr); knot_rdata_t tmp_rr[knot_rdata_array_size(size)]; knot_rdata_init(tmp_rr, size, rdata, ttl); // Move the array or just part of it knot_rdata_t *earlier_rr_moved = earlier_rr + knot_rdata_array_size(size); size_t last_rr_size = knot_rdata_array_size(knot_rdata_rdlen(last_rr)); memmove(earlier_rr_moved, earlier_rr, (last_rr + last_rr_size) - earlier_rr); // Set new RR knot_rdata_init(earlier_rr, size, knot_rdata_data(tmp_rr), ttl); return KNOT_EOK; }
/*----------------------------------------------------------------------------*/ _public_ bool knot_edns_check_record(knot_rrset_t *opt_rr) { if (opt_rr->rrs.rr_count != 1) { return false; } knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0); if (rdata == NULL) { return false; } uint8_t *data = knot_rdata_data(rdata); uint16_t rdlength = knot_rdata_rdlen(rdata); uint32_t pos = 0; /* RFC2671 4.4: {uint16_t code, uint16_t len, data} */ while (pos + KNOT_EDNS_OPTION_HDRLEN <= rdlength) { uint16_t opt_len = wire_read_u16(data + pos + sizeof(uint16_t)); pos += KNOT_EDNS_OPTION_HDRLEN + opt_len; } /* If not at the end of the RDATA, there are either some redundant data * (pos < rdlength) or the last OPTION is too long (pos > rdlength). */ return pos == rdlength; }
/*----------------------------------------------------------------------------*/ _public_ int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code, uint16_t length, const uint8_t *data, mm_ctx_t *mm) { if (opt_rr == NULL || (length != 0 && data == NULL)) { return KNOT_EINVAL; } /* We need to replace the RDATA currently in the OPT RR */ /* 1) create new RDATA by appending the new option after the current * RDATA. */ assert(opt_rr->rrs.rr_count == 1); knot_rdata_t *old_rdata = knot_rdataset_at(&opt_rr->rrs, 0); uint8_t *old_data = knot_rdata_data(old_rdata); uint16_t old_data_len = knot_rdata_rdlen(old_rdata); uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + length; uint8_t new_data[new_data_len]; memcpy(new_data, old_data, old_data_len); // write length and code in wireformat (convert endian) wire_write_u16(new_data + old_data_len, code); wire_write_u16(new_data + old_data_len + sizeof(uint16_t), length); // write the option data memcpy(new_data + old_data_len + KNOT_EDNS_OPTION_HDRLEN, data, length); /* 2) Replace the RDATA in the RRSet. */ uint32_t old_ttl = knot_rdata_ttl(old_rdata); knot_rdataset_clear(&opt_rr->rrs, mm); return knot_rrset_add_rdata(opt_rr, new_data, new_data_len, old_ttl, mm); }
int main(int argc, char *argv[]) { plan(9); // Test array size size_t array_size = knot_rdata_array_size(16); ok(array_size == sizeof(struct rr_offsets) + 16, "rdata: array size."); // Test init knot_rdata_t rdata[array_size]; uint8_t payload[16] = "abcdefghijklmnop"; knot_rdata_init(rdata, 16, payload, 3600); const bool set_ok = knot_rdata_rdlen(rdata) == 16 && knot_rdata_ttl(rdata) == 3600 && memcmp(knot_rdata_data(rdata), payload, 16) == 0; ok(set_ok, "rdata: init."); // Test setters knot_rdata_set_ttl(rdata, 1234); ok(knot_rdata_ttl(rdata) == 1234, "rdata: set TTL."); knot_rdata_set_rdlen(rdata, 1); ok(knot_rdata_rdlen(rdata) == 1, "rdata: set RDLEN."); // Test compare knot_rdata_set_rdlen(rdata, 16); ok(knot_rdata_cmp(rdata, rdata) == 0, "rdata: cmp eq."); knot_rdata_t *lower = rdata; knot_rdata_t greater[knot_rdata_array_size(16)]; knot_rdata_init(greater, 16, (uint8_t *)"qrstuvwxyz123456", 1234); ok(knot_rdata_cmp(lower, greater) < 0, "rdata: cmp lower."); ok(knot_rdata_cmp(greater, lower) > 0, "rdata: cmp greater."); // Payloads will be the same. memcpy(knot_rdata_data(greater), knot_rdata_data(lower), 16); assert(knot_rdata_cmp(lower, greater) == 0); knot_rdata_set_rdlen(lower, 15); ok(knot_rdata_cmp(lower, greater) < 0, "rdata: cmp lower size."); ok(knot_rdata_cmp(greater, lower) > 0, "rdata: cmp greater size."); return 0; }
static void print_section_opt(const knot_rrset_t *rr, const uint8_t rcode) { uint8_t ercode = knot_edns_get_ext_rcode(rr); uint16_t ext_rcode_id = knot_edns_whole_rcode(ercode, rcode); const char *ext_rcode_str = "Unused"; lookup_table_t *ext_rcode; if (ercode > 0) { ext_rcode = lookup_by_id(knot_rcode_names, ext_rcode_id); if (ext_rcode != NULL) { ext_rcode_str = ext_rcode->name; } else { ext_rcode_str = "Unknown"; } } printf("Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n", knot_edns_get_version(rr), (knot_edns_do(rr) != 0) ? "do" : "", knot_edns_get_payload(rr), ext_rcode_str); knot_rdata_t *rdata = knot_rdataset_at(&rr->rrs, 0); assert(rdata != NULL); uint16_t data_len = knot_rdata_rdlen(rdata); uint8_t *data = knot_rdata_data(rdata); int pos = 0; while (pos < data_len - KNOT_EDNS_OPTION_HDRLEN) { uint16_t opt_code = wire_read_u16(data + pos); uint16_t opt_len = wire_read_u16(data + pos + 2); uint8_t *opt_data = data + pos + 4; switch (opt_code) { case KNOT_EDNS_OPTION_NSID: printf(";; NSID: "); short_hex_print(opt_data, opt_len); if (opt_len > 0) { printf(";; : "); txt_print(opt_data, opt_len); } break; case KNOT_EDNS_OPTION_CLIENT_SUBNET: printf(";; CLIENT-SUBNET: "); print_edns_client_subnet(opt_data, opt_len); break; default: printf(";; Option (%u): ", opt_code); short_hex_print(opt_data, opt_len); } pos += 4 + opt_len; } }
static bool check_option(knot_rdata_t *rdata, uint16_t opt_code, uint16_t opt_len, uint8_t *opt_data, char *msg, int *done) { assert(rdata != NULL); bool success = true; uint8_t *data = knot_rdata_data(rdata); uint16_t data_len = knot_rdata_rdlen(rdata); /* Check RDLENGTH according to given data length. */ bool check = (data_len >= 4 + opt_len); ok(check, "%s: RDLENGTH (%u)", msg, data_len); success &= check; (*done)++; /* Find the desired option. */ bool found = false; int pos = 0; while (pos <= data_len - 4) { uint16_t code = wire_read_u16(data + pos + OFFSET_OPT_CODE); if (code == opt_code) { found = true; break; } uint16_t len = wire_read_u16(data + pos + OFFSET_OPT_SIZE); pos += 4 + len; } /* Check that the option is present. */ ok(found, "%s: find OPTION %u in OPT RR", msg, opt_code); success &= found; (*done)++; /* Check that the first OPTION's size si the size of the option data. */ uint16_t opt_size = wire_read_u16(data + pos + OFFSET_OPT_SIZE); check = (opt_size == opt_len); ok(check, "%s: OPTION data size", msg); success &= check; (*done)++; /* Check the actual NSID data. */ check = (memcmp(data + pos + OFFSET_OPT_DATA, opt_data, opt_len)) == 0; ok(check, "%s: OPTION data", msg); success &= check; (*done)++; return success; }
/*! * \brief Create and zero SIG(0) RDATA field. * * \param rrset SIG(0) RR set. * \param key Signing key. * * \return SIG(0) RDATA. */ static uint8_t *sig0_create_rdata(knot_rrset_t *rrset, knot_dnssec_key_t *key) { assert(rrset); assert(key); size_t rdata_size = knot_rrsig_rdata_size(key); const uint32_t ttl = 0; uint8_t rdata[rdata_size]; sig0_write_rdata(rdata, key); if (knot_rrset_add_rdata(rrset, rdata, rdata_size, ttl, NULL) != KNOT_EOK) { return NULL; } const knot_rdata_t *rr = knot_rdataset_at(&rrset->rrs, 0); return knot_rdata_data(rr); }
static int add_rr_at(knot_rdataset_t *rrs, const knot_rdata_t *rr, size_t pos, knot_mm_t *mm) { if (rrs == NULL || pos > rrs->rr_count) { return KNOT_EINVAL; } const uint16_t size = knot_rdata_rdlen(rr); const uint32_t ttl = knot_rdata_ttl(rr); const uint8_t *rdata = knot_rdata_data(rr); size_t total_size = knot_rdataset_size(rrs); // Realloc data. void *tmp = mm_realloc(mm, rrs->data, total_size + knot_rdata_array_size(size), total_size); if (tmp) { rrs->data = tmp; } else { return KNOT_ENOMEM; } if (rrs->rr_count == 0 || pos == rrs->rr_count) { // No need to rearange RDATA rrs->rr_count++; knot_rdata_t *new_rr = knot_rdataset_at(rrs, pos); knot_rdata_init(new_rr, size, rdata, ttl); return KNOT_EOK; } // RDATA have to be rearanged. knot_rdata_t *last_rr = knot_rdataset_at(rrs, rrs->rr_count - 1); knot_rdata_t *old_rr = knot_rdataset_at(rrs, pos); assert(last_rr); assert(old_rr); // Make space for new data by moving the array memmove(old_rr + knot_rdata_array_size(size), old_rr, (last_rr + knot_rdata_array_size(knot_rdata_rdlen(last_rr))) - old_rr); // Set new RR knot_rdata_init(old_rr, size, rdata, ttl); rrs->rr_count++; return KNOT_EOK; }
int kr_check_signature(const knot_rrset_t *rrsigs, size_t pos, const dnssec_key_t *key, const knot_rrset_t *covered, int trim_labels) { if (!rrsigs || !key || !dnssec_key_can_verify(key)) { return kr_error(EINVAL); } int ret = 0; dnssec_sign_ctx_t *sign_ctx = NULL; dnssec_binary_t signature = {0, }; knot_rrsig_signature(&rrsigs->rrs, pos, &signature.data, &signature.size); if (!signature.data || !signature.size) { ret = kr_error(EINVAL); goto fail; } if (dnssec_sign_new(&sign_ctx, key) != 0) { ret = kr_error(ENOMEM); goto fail; } uint32_t orig_ttl = knot_rrsig_original_ttl(&rrsigs->rrs, pos); const knot_rdata_t *rr_data = knot_rdataset_at(&rrsigs->rrs, pos); uint8_t *rdata = knot_rdata_data(rr_data); if (sign_ctx_add_data(sign_ctx, rdata, covered, orig_ttl, trim_labels) != 0) { ret = kr_error(ENOMEM); goto fail; } if (dnssec_sign_verify(sign_ctx, &signature) != 0) { ret = kr_error(EBADMSG); goto fail; } ret = kr_ok(); fail: dnssec_sign_free(sign_ctx); return ret; }
int kr_dnskeys_trusted(const knot_pkt_t *pkt, knot_section_t section_id, const knot_rrset_t *keys, const knot_rrset_t *ta, const knot_dname_t *zone_name, uint32_t timestamp, bool has_nsec3) { if (!pkt || !keys || !ta) { return kr_error(EINVAL); } /* RFC4035 5.2, bullet 1 * The supplied DS record has been authenticated. * It has been validated or is part of a configured trust anchor. */ for (uint16_t i = 0; i < keys->rrs.rr_count; ++i) { /* RFC4035 5.3.1, bullet 8 */ /* ZSK */ const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, i); const uint8_t *key_data = knot_rdata_data(krr); if (!kr_dnssec_key_zsk(key_data) || kr_dnssec_key_revoked(key_data)) { continue; } struct dseckey *key; if (kr_dnssec_key_from_rdata(&key, keys->owner, key_data, knot_rdata_rdlen(krr)) != 0) { continue; } if (kr_authenticate_referral(ta, (dnssec_key_t *) key) != 0) { kr_dnssec_key_free(&key); continue; } if (kr_rrset_validate_with_key(pkt, section_id, keys, keys, i, key, zone_name, timestamp, has_nsec3) != 0) { kr_dnssec_key_free(&key); continue; } kr_dnssec_key_free(&key); return kr_ok(); } /* No useable key found */ return kr_error(ENOENT); }
/*! * \brief Find OPTION with the given code in the OPT RDATA. * * \param rdata RDATA to search in. * \param opt_code Code of the OPTION to find. * \param[out] pos Position of the OPTION or NULL if not found. */ static uint8_t *find_option(knot_rdata_t *rdata, uint16_t opt_code) { assert(rdata != NULL); uint8_t *data = knot_rdata_data(rdata); uint16_t rdlength = knot_rdata_rdlen(rdata); uint8_t *pos = NULL; int i = 0; while (i + KNOT_EDNS_OPTION_HDRLEN <= rdlength) { uint16_t code = wire_read_u16(data + i); if (opt_code == code) { pos = data + i; break; } uint16_t opt_len = wire_read_u16(data + i + sizeof(uint16_t)); i += (KNOT_EDNS_OPTION_HDRLEN + opt_len); } return pos; }
int kr_rrset_validate_with_key(const knot_pkt_t *pkt, knot_section_t section_id, const knot_rrset_t *covered, const knot_rrset_t *keys, size_t key_pos, const struct dseckey *key, const knot_dname_t *zone_name, uint32_t timestamp, bool has_nsec3) { struct dseckey *created_key = NULL; if (key == NULL) { const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, key_pos); int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner, knot_rdata_data(krr), knot_rdata_rdlen(krr)); if (ret != 0) { return ret; } key = created_key; } uint16_t keytag = dnssec_key_get_keytag((dnssec_key_t *)key); int covered_labels = knot_dname_labels(covered->owner, NULL); if (knot_dname_is_wildcard(covered->owner)) { /* The asterisk does not count, RFC4034 3.1.3, paragraph 3. */ --covered_labels; } const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); for (unsigned i = 0; i < sec->count; ++i) { /* Consider every RRSIG that matches owner and covers the class/type. */ const knot_rrset_t *rrsig = knot_pkt_rr(sec, i); if (rrsig->type != KNOT_RRTYPE_RRSIG) { continue; } if ((covered->rclass != rrsig->rclass) || !knot_dname_is_equal(covered->owner, rrsig->owner)) { continue; } for (uint16_t j = 0; j < rrsig->rrs.rr_count; ++j) { int val_flgs = 0; int trim_labels = 0; if (knot_rrsig_type_covered(&rrsig->rrs, j) != covered->type) { continue; } if (validate_rrsig_rr(&val_flgs, covered_labels, rrsig, j, keys, key_pos, keytag, zone_name, timestamp) != 0) { continue; } if (val_flgs & FLG_WILDCARD_EXPANSION) { trim_labels = wildcard_radix_len_diff(covered->owner, rrsig, j); if (trim_labels < 0) { break; } } if (kr_check_signature(rrsig, j, (dnssec_key_t *) key, covered, trim_labels) != 0) { continue; } if (val_flgs & FLG_WILDCARD_EXPANSION) { int ret = 0; if (!has_nsec3) { ret = kr_nsec_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner); } else { ret = kr_nsec3_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner, trim_labels - 1); } if (ret != 0) { continue; } } /* Validated with current key, OK */ kr_dnssec_key_free(&created_key); return kr_ok(); } } /* No applicable key found, cannot be validated. */ kr_dnssec_key_free(&created_key); return kr_error(ENOENT); }