static int sign_ctx_add_self(dnssec_sign_ctx_t *ctx, const uint8_t *rdata) { assert(ctx); assert(rdata); int result; // static header dnssec_binary_t header = { 0 }; header.data = (uint8_t *)rdata; header.size = RRSIG_RDATA_SIGNER_OFFSET; result = dnssec_sign_add(ctx, &header); if (result != DNSSEC_EOK) { return result; } // signer name const uint8_t *rdata_signer = rdata + RRSIG_RDATA_SIGNER_OFFSET; dnssec_binary_t signer = { 0 }; signer.data = knot_dname_copy(rdata_signer, NULL); signer.size = knot_dname_size(signer.data); result = dnssec_sign_add(ctx, &signer); free(signer.data); return result; }
/*----------------------------------------------------------------------------*/ _public_ int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld, uint8_t ext_rcode, uint8_t ver, mm_ctx_t *mm) { if (opt_rr == NULL) { return KNOT_EINVAL; } /* Initialize RRSet. */ knot_dname_t *owner = knot_dname_copy((const uint8_t*)"", mm); if (owner == NULL) { return KNOT_ENOMEM; } knot_rrset_init(opt_rr, owner, KNOT_RRTYPE_OPT, max_pld); /* Create empty RDATA */ int ret = knot_rrset_add_rdata(opt_rr, NULL, 0, 0, mm); if (ret == KNOT_EOK) { knot_edns_set_ext_rcode(opt_rr, ext_rcode); knot_edns_set_version(opt_rr, ver); } return ret; }
static int answer_query(knot_pkt_t *pkt, pack_t *addr_set, struct kr_query *qry) { uint16_t rrtype = qry->stype; uint16_t rrclass = qry->sclass; if (rrtype != KNOT_RRTYPE_A && rrtype != KNOT_RRTYPE_AAAA) { return kr_error(ENOENT); } knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm); knot_rrset_t rr; knot_rrset_init(&rr, qname, rrtype, rrclass); int family_len = sizeof(struct in_addr); if (rr.type == KNOT_RRTYPE_AAAA) { family_len = sizeof(struct in6_addr); } /* Append address records from hints */ uint8_t *addr = pack_head(*addr_set); while (addr != pack_tail(*addr_set)) { size_t len = pack_obj_len(addr); void *addr_val = pack_obj_val(addr); if (len == family_len) { knot_rrset_add_rdata(&rr, addr_val, len, 0, &pkt->mm); } addr = pack_obj_next(addr); } int ret = kr_error(ENOENT); if (!knot_rrset_empty(&rr)) { /* Update packet question */ if (!knot_dname_is_equal(knot_pkt_qname(pkt), qname)) { KR_PKT_RECYCLE(pkt); knot_pkt_put_question(pkt, qname, rrclass, rrtype); } /* Append to packet */ ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &rr, KNOT_PF_FREE); } /* Clear RR if failed */ if (ret != 0) { knot_rrset_clear(&rr, &pkt->mm); } return ret; }
static int validate(knot_layer_t *ctx, knot_pkt_t *pkt) { int ret = 0; struct kr_request *req = ctx->data; struct kr_query *qry = req->current_query; /* Ignore faulty or unprocessed responses. */ if (ctx->state & (KNOT_STATE_FAIL|KNOT_STATE_CONSUME)) { return ctx->state; } /* Pass-through if user doesn't want secure answer or stub. */ /* @todo: Validating stub resolver mode. */ if (!(qry->flags & QUERY_DNSSEC_WANT) || (qry->flags & QUERY_STUB)) { return ctx->state; } /* Answer for RRSIG may not set DO=1, but all records MUST still validate. */ bool use_signatures = (knot_pkt_qtype(pkt) != KNOT_RRTYPE_RRSIG); if (!(qry->flags & QUERY_CACHED) && !knot_pkt_has_dnssec(pkt) && !use_signatures) { DEBUG_MSG(qry, "<= got insecure response\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } /* Track difference between current TA and signer name. * This indicates that the NS is auth for both parent-child, and we must update DS/DNSKEY to validate it. */ const bool track_pc_change = (!(qry->flags & QUERY_CACHED) && (qry->flags & QUERY_DNSSEC_WANT)); const knot_dname_t *ta_name = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL; const knot_dname_t *signer = signature_authority(pkt); if (track_pc_change && ta_name && (!signer || !knot_dname_is_equal(ta_name, signer))) { if (ctx->state == KNOT_STATE_YIELD) { /* Already yielded for revalidation. */ return KNOT_STATE_FAIL; } DEBUG_MSG(qry, ">< cut changed, needs revalidation\n"); if (!signer) { /* Not a DNSSEC-signed response, ask parent for DS to prove transition to INSECURE. */ } else if (knot_dname_is_sub(signer, qry->zone_cut.name)) { /* Key signer is below current cut, advance and refetch keys. */ qry->zone_cut.name = knot_dname_copy(signer, &req->pool); } else if (!knot_dname_is_equal(signer, qry->zone_cut.name)) { /* Key signer is above the current cut, so we can't validate it. This happens when a server is authoritative for both grandparent, parent and child zone. Ascend to parent cut, and refetch authority for signer. */ if (qry->zone_cut.parent) { memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut)); } else { qry->flags |= QUERY_AWAIT_CUT; } qry->zone_cut.name = knot_dname_copy(signer, &req->pool); } /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */ return KNOT_STATE_YIELD; } /* Check if this is a DNSKEY answer, check trust chain and store. */ uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire); uint16_t qtype = knot_pkt_qtype(pkt); bool has_nsec3 = pkt_has_type(pkt, KNOT_RRTYPE_NSEC3); if (knot_wire_get_aa(pkt->wire) && qtype == KNOT_RRTYPE_DNSKEY) { ret = validate_keyset(qry, pkt, has_nsec3); if (ret != 0) { DEBUG_MSG(qry, "<= bad keys, broken trust chain\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } /* Validate non-existence proof if not positive answer. */ if (!(qry->flags & QUERY_CACHED) && pkt_rcode == KNOT_RCODE_NXDOMAIN) { /* @todo If knot_pkt_qname(pkt) is used instead of qry->sname then the tests crash. */ if (!has_nsec3) { ret = kr_nsec_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname); } else { ret = kr_nsec3_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname); } if (ret != 0) { DEBUG_MSG(qry, "<= bad NXDOMAIN proof\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } /* @todo WTH, this needs API that just tries to find a proof and the caller * doesn't have to worry about NSEC/NSEC3 * @todo rework this */ if (!(qry->flags & QUERY_CACHED)) { const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER); if (pkt_rcode == KNOT_RCODE_NOERROR && an->count == 0 && knot_wire_get_aa(pkt->wire)) { /* @todo * ? quick mechanism to determine which check to preform first * ? merge the functionality together to share code/resources */ if (!has_nsec3) { ret = kr_nsec_existence_denial(pkt, KNOT_AUTHORITY, knot_pkt_qname(pkt), knot_pkt_qtype(pkt)); } else { ret = kr_nsec3_no_data(pkt, KNOT_AUTHORITY, knot_pkt_qname(pkt), knot_pkt_qtype(pkt)); } if (ret != 0) { if (has_nsec3 && (ret == kr_error(DNSSEC_NOT_FOUND))) { DEBUG_MSG(qry, "<= can't prove NODATA due to optout, going insecure\n"); qry->flags &= ~QUERY_DNSSEC_WANT; qry->flags |= QUERY_DNSSEC_INSECURE; } else { DEBUG_MSG(qry, "<= bad NODATA proof\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } } } /* Validate all records, fail as bogus if it doesn't match. * Do not revalidate data from cache, as it's already trusted. */ if (!(qry->flags & QUERY_CACHED)) { ret = validate_records(qry, pkt, req->rplan.pool, has_nsec3); if (ret != 0) { DEBUG_MSG(qry, "<= couldn't validate RRSIGs\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } /* Check if wildcard expansion detected for final query. * If yes, copy authority. */ if ((qry->parent == NULL) && (qry->flags & QUERY_DNSSEC_WEXPAND)) { const knot_pktsection_t *auth = knot_pkt_section(pkt, KNOT_AUTHORITY); for (unsigned i = 0; i < auth->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(auth, i); kr_rrarray_add(&req->authority, rr, &req->answer->mm); } } /* Check and update current delegation point security status. */ ret = update_delegation(req, qry, pkt, has_nsec3); if (ret != 0) { return KNOT_STATE_FAIL; } /* Update parent query zone cut */ if (qry->parent) { if (update_parent_keys(qry, qtype) != 0) { return KNOT_STATE_FAIL; } } DEBUG_MSG(qry, "<= answer valid, OK\n"); return KNOT_STATE_DONE; }